Thursday, July 1, 2010

How to create a generic Dao with Hibernate and Spring Framework

When you are designing web applications a common problem to solve is the DAO (Data Access Object) layer. The simpler way to implement a DAO layer with Hibernate in seconds is to use some of the classes that are provided with Hibernate such as HibernateTemplate. HibernateTemplate class simplifies the data access operations when using Hibernate and provides an easy way to handle Hibernate Session objects .The following diagram shows in UML what is the rationale behind this implementation.


Image 1.  UML diagram for a generic DAO.


The solution proposed in this article is based on Spring Framework 2.5 and Hibernate 3.2.x. Let's begin with a business model class called Person, its objects (instances) are going to be used and obtained from a database like Oracle, MSSQL, or whatever JDBC compliant database
.

Listing 1. Java Implementation of the business model class.
public class Person {
      private Long id;
      private String name;
      private int age;
      public Person() {
      }
      public void setAge(final int age) {
            this.age = age;
      }
      public void setId(final Long id) {
            this.id = id;
      }
      public void setName(final String name) {
            this.name = name;
      }
      public int getAge() {
            return age;
      }
      public Long getId() {
            return id;
      }
      public String getName() {
            return name;
      }
}

Listing 2. Hibernate XML Mapping.




<?xml version="1.0" encoding="ISO-8859-1"?>
DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-access="field" default-cascade="save-update">
    <class name="Person" table="Person">
        <id column="ID" name="id">
            <generator class="native"/>
        </id>
        <property name="name"/>
       <property name="age"/>
    </class>
</hibernate-mapping>

Next, we have to define the GenericDao interface where we are going to declare CRUD (Create, Retrieve, Update and Delete) operations. This interface is declared with one Generic Type, that is a template for the Business Model class (for example a Person class).

Listing 3.  Generic DAO Interface.
public interface GenericDao {
     public void save(T entity);
     public void delete(T entity);
     public void update(T entity);
     public T findById(Serializable id);
     public List findAll();
     public void deleteById(Serializable id);
     public int count();
     public List findByExample(T exampleObject);
}

After doing this, you will need to implement this GenericDao interface in order to bring the specific HibernateGenericDAO implementation of the CRUD methods. We extend HibernateDaoSupport because it provides some useful functions such as a exception translation hierarchy, and we declare HibernateGenericDAO as abstract because we are not interested in creating instances of this class (we are interested in creating specific business model classes DAO instances).

Listing 4.  GenericDAO implementation in Hibernate.
public abstract class HibernateGenericDAO extends HibernateDaoSupport implements GenericDao {
    protected Class persistentClass = getDomainClass();
    protected abstract Class getDomainClass();
    public void save(final T entity) {
        getHibernateTemplate().save(entity);
    }
    public void delete(final T entity) {
        getHibernateTemplate().delete(entity);
    }
    public void update(final T entity) {
        getHibernateTemplate().update(entity);
    }
    @SuppressWarnings("unchecked")
    public T findById(final Serializable id) {
        return (T) getHibernateTemplate().get(this.persistentClass, id);
    }
    @SuppressWarnings("unchecked")
    public List findAll() {
        return getHibernateTemplate().find("from " + this.persistentClass.getName() + " o");
    }
    public void deleteById(final Serializable id) {
        T obj = this.findById(id);
        getHibernateTemplate().delete(obj);
    }
    @SuppressWarnings("unchecked")
    public int count() {
        List list = getHibernateTemplate().find("select count(*) from " + persistentClass.getName() + " o");
        Long count = list.get(0);
        return count.intValue();
    }
    @SuppressWarnings("unchecked")
    public List findByExample(final T exampleObject) {
        return getHibernateTemplate().findByExample(exampleObject);
    }
}

After creating the last class the only thing that you will need is to create the specific business model DAO class, in our case PersonDAO. You can realize that it is a very short class, this is the advantage of using the GenericDAO. Every single DAO class for a specifi business model class is going to be as short as this one.

Listing 5. Business Model Class Data Access Object.
public class PersonDAO extends HibernateGenericDAO implements GenericDao {
    @Override
    protected Class getDomainClass() {
        return Person.class;
    }
}

Well, we already have the foundation needed to integrate with spring framework. Spring will be responsible to bind all the dependencies of the beans (glue it all together) and to handle the instances.

Listing 6. Spring framework DAO Configuration
<?xml version="1.0" encoding="UTF-8"?>



<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
      <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                  <list>
                        <value>classpath*:META-INF/jdbc.propertiesvalue>
                        <value>classpath*:META-INF/hibernate.propertiesvalue>
                  </list>
            </property>
      <bean>
      <bean id="internal.theDataSource"
            class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close">
            <property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.db.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
<property name="maxActive" value="100"/>
<property name="maxWait" value="1000"/>
        <property name="poolPreparedStatements" value="true"/>
        <property name="defaultAutoCommit" value="true"/>
      </bean>
      <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy" lazy-init="true">
            <property name="targetDataSource" ref="internal.theDataSource" />
      </bean>
      <bean id="persistence.sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" lazy-init="true">
            <property name="dataSource" ref="dataSource" />
            <property name="mappingLocations">
                  <list>
                        <value>classpath*:mappings/*.hbm.xmlvalue>
                  </list>
            <property>
            <property name="hibernateProperties">
                  <props>
                        <prop key="hibernate.show_sql">
                              ${hibernate.show_sql}
                        </prop>
                        <prop key="hibernate.dialect">
                              ${hibernate.dialect}
                        </prop>
                        <prop key="hibernate.hbm2ddl.auto">
                              ${hibernate.hbm2ddl.auto}
                        </prop>
                        <prop key="hibernate.cglib.use_reflection_optimizer">
                              ${hibernate.cglib.use_reflection_optimizer}
                        </prop>
                        <prop key="connection.pool_size">
                              ${hibernate.connection.pool_size}
                        </prop>
                        <prop key="hibernate.current_session_context_class">
                              ${hibernate.current_session_context_class}
                        </prop>
                        <prop key="hibernate.transaction.flush_before_completion">
                              ${hibernate.transaction.flush_before_completion}
                        </prop>
                  </props>
            </property>
      </bean>
      <bean id="persistence.personDAO" class="com.hexacta.persistence.dao.PersonDAO" lazy-init="default" autowire="default" dependency-check="default">
            <property name="sessionFactory">
            <ref bean="persistence.sessionFactory"/>
            </property>
      </bean>
</beans>

No comments:

Post a Comment