Shekhar Gulati is a Java Consultant with over 5 years experience.He is currently working with Xebia India an Agile Software Development company.The postings on this site and on his blog are his own and do not necessarily represent opinion of his employer. His own blog is at http://whyjava.wordpress.com/ and you can follow him on twitter here. Shekhar has posted 25 posts at DZone. View Full User Profile

Reduce Boilerplate Code for DAO's -- Hades Introduction

10.15.2010
| 13228 views |
  • submit to reddit

Most web applications will have DAO's for accessing the database layer. A DAO provides an interface for some type of database or persistence mechanism, providing CRUD and finders operations without exposing any database details. So, in your application you will have different DAO's for different entities. Most of the time, code that you have written in one DAO will get duplicated in other DAO's because much of the functionality in DAO's is same (like CRUD and finder methods).

One of way of avoiding this problem is to have generic DAO and have your domain classes inherit this generic DAO implementation. You can also add finders using Spring AOP; this approach is explained Per Mellqvist in this article. There is a problem with the approach: this boiler plate code becomes part of your application source code and you will have to maintain it. The more code you write, there are more chances of new bugs getting introduced in your application. So, to avoid writing this code in an application, we can use an open source framework called Hades.

Hades is a utility library to work with Data Access Objects implemented with Spring and JPA. The main goal is to ease the development and operation of a data access layer in applications.  In this article, I will show you how easy it is write DAO's using Hades without writing any boiler plate code.

In order to introduce you to Hades, I will show you how we can manage an entity like Book. Before we write any code we need to add the following dependencies to pom.xml.

<dependency>
<groupId>org.synyx.hades</groupId>
<artifactId>org.synyx.hades</artifactId>
<version>2.0.0.RC3</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.5.5-Final</version>
</dependency>

 So, lets start by creating a Book Entity

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Book {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Column(unique = true)
private String title;

private String author;
private String isbn;
private double price;

// setters and getters

}

This is a very simple JPA entity without any relationships. Now that we have modeled our entity we need to add a DAO interface for handling persistence operations. You need to create a BookDao interface which will extend GenericDao interface provided by Hades.  GenericDao is an interface for generic CRUD operations on a DAO for a specific type.  So, we passed the type parameters Book for entity and Long for id.

import org.synyx.hades.dao.GenericDao;

public interface BookDao extends GenericDao<Book, Long> {

}

GenericDao has an default implementation called GenericJpaDao which provides implementation of all its operations. Now that we have created a BookDao interface, we will  configure it in the Spring application context xml. Hades provides a factory bean which will provide the DAO instance for the given interface (in our case BookDao).

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd">

<bean id="bookDao" class="org.synyx.hades.dao.orm.GenericDaoFactoryBean">
<property name="daoInterface" value="com.shekhar.hades.BookDao"></property>
</bean>

<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<jdbc:embedded-database id="dataSource" type="HSQL" />
</beans>

In the xml shown above I have used a new feature introduced in Spring 3 Embedded Databases to give me the instance of HSQL database datasource. You can refer to my earlier post on Embedded databases in case you are not aware of it. I have used Hibernate as my JPA provider so you need to configure it in persistence.xml as shown below

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
<!--value='create' to build a new database on each run; value='update' to modify an existing database; value='create-drop' means the same as 'create' but also drops tables when Hibernate closes; value='validate' makes no changes to the database-->
<property name="hibernate.hbm2ddl.auto" value="create"/>
<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/>
</properties>
</persistence-unit>

</persistence>

 Next we will write a JUnit test for testing this code.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
public class BookDaoTest {

@Autowired
private BookDao bookDao;
private Book book;

@Before
public void setUp() throws Exception {
book = new Book();
book.setAuthor("shekhar");
book.setTitle("Effective Java");
book.setPrice(500);
book.setIsbn("1234567890123");
}

@Test
public void shouldCreateABook() throws Exception {
Book persistedBook = bookDao.save(book);
assertBook(persistedBook);
}

@Test
public void shouldReadAPersistedBook() throws Exception {
Book persistedBook = bookDao.save(book);
Book bookReadByPrimaryKey = bookDao.readByPrimaryKey(persistedBook.getId());
assertBook(bookReadByPrimaryKey);
}

@Test
public void shouldDeleteBook() throws Exception {
Book persistedBook = bookDao.save(book);
bookDao.delete(persistedBook);
Book bookReadByPrimaryKey = bookDao.readByPrimaryKey(persistedBook.getId());
assertNull(bookReadByPrimaryKey);
}

private void assertBook(Book persistedBook) {
assertThat(persistedBook, is(notNullValue()));
assertThat(persistedBook.getId(), is(not(equalTo(null))));
assertThat(persistedBook.getAuthor(), equalTo(book.getAuthor()));
}
}

Auto Configuration Using Spring Namespaces

The way that we have configured the DAO can become quite cumbersome if the number of DAO's increases. To overcome this we can make use of namespaces to configure daos.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:hades="http://schemas.synyx.org/hades"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://schemas.synyx.org/hades http://schemas.synyx.org/hades/hades.xsd">

<hades:dao-config base-package="com.shekhar.hades"
entity-manager-factory-ref="entityManagerFactory"></hades:dao-config>

<!-- All other things remain same -->
</beans>

This configuration will trigger the auto detection mechanism of DAOs that extend GenericDAO or Extended-GenericDAO. It will create DAO instances for all the DAO interfaces found in this package. You can use <include-filter> or <exclude-filter> for including or excluding interfaces from getting their beans created.

Adding Finders and Query Methods

So far we have used the inbuilt operations provided by GenericDao but most of the time we need to add our own finders methods like findByAuthorAndTitle, findWithPriceLessThan . Hades makes it very easy for you to add such methods in your domain dao interface like BookDao. Hades provides 3 strategies for creating JPA query at runtime. These are :-

  1. CREATE : This will create a JPA query from method name. This strategy ties you with the method name so you have to think twice before changing the method name.

    public interface BookDao extends GenericDao<Book, Long> {

    public Book findByAuthorAndTitle(String author, String title);
    }

    // test code
    @Test
    public void shouldFindByAuthorAndTitle() throws Exception {
    Book persistedBook = bookDao.save(book);
    Book bookByAuthorAndTitle = bookDao.findByAuthorAndTitle("shekhar", "Effective Java");
    assertBook(bookByAuthorAndTitle);
    }
  2. USE DECLARED QUERY : This lets you define query using JPA @NamedQuery  or Hades @Query annotation. If no query is found exception will be thrown.

     @Query("FROM Book b WHERE b.author = ?1")
    public Book findBookByAuthorName(String author);

    // test code
    public void shouldFindBookByAuthorName() {
    Book persistedBook = bookDao.save(book);
    Book bookByAuthor = bookDao.findBookByAuthorName("shekhar");
    assertBook(bookByAuthor);
    }
  3. CREATE IF NOT FOUND : It is the combination of both the strategies mentioned above. It will first lookup for the declared query and if it is not found will lookup for method name. This is by default option and you can change it by changing query-lookup-strategy attribute in hades:dao-config element. 

Hades queries also has the support for pagination and sorting. You can pass the instance of Pageable and Sort to the finder methods create above.

public Page<Book> findByAuthor(String author, Pageable pageable);
public List<Book> findByAuthor(String author, Sort sort);

Hades is not limited to just limited to CRUD and adding custom finder methods. There are some other features like auditing ,Specifications,etc that I will discuss in second part of this article.

Published at DZone with permission of its author, Shekhar Gulati.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Oliver Gierke replied on Fri, 2010/10/15 - 11:17am

Hi Shekar, thank you very much for this article. I was about write some introductory article as well preparing the 2.0 GA release but it seems now I can concentrate on explaining what's actually new in 2.0 over the previous major version. Looking forward to the second part of your article. Thanks for spreading the word. Ollie Hades project lead

Shekhar Gulati replied on Fri, 2010/10/15 - 1:15pm in response to: Oliver Gierke

Thanks Oliver for your words.

Tomasz Nurkiewicz replied on Fri, 2010/10/15 - 2:04pm

Hades is a really nice library, I used it in few projects recently and it worked well. I also wrote a similar introduction, if you want to grab a different example: http://nurkiewicz.blogspot.com/2010/07/hades-dry-principle-in-jpaspring.html

Shirish Pandharikar replied on Sun, 2010/10/17 - 11:10pm

Hi,

      Looks more or less similar to MyBatis (iBatis 3) Mapper concept. Hades is indeed useful library for JPA projects.

Shirish

E Da replied on Thu, 2012/05/31 - 9:29am

http://www.ibm.com/developerworks/java/library/j-genericdao/index.html

The mechanism described in this article, in May 2006, looks almost similar with the Hades support :)

E Da replied on Fri, 2012/06/01 - 7:14am

Hi,

As I read about Hades, it seems to have transactional behavior by default for CRUD operations. Is there any way to change this and turn it off? I prefer to use transactions only in service layer not in DAO layer...

Thanx

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.