Java 1.5 Generic DAO

by Dana H. P'Simer

Fri, Oct 6, 2006


I am always looking for ways to limit the amount of typing I have to do. Hibernate helps me alot because I don’t have to write all the SQL and I can concentrate on the less tedious aspects of designing my persistence model. Spring helps me alot because I don’t have to write all those factories and other “glue” code.

In the interests of getting more done quickly, I started looking at how I can automate the generatin of the DAO pattern. Data Access Objects allow the service or session facade layer to be agnostic to the persistence layer used. As I have already said, I use hibernate but there might come a time when I need to use some other persistence layer and by keeping all the persistence layer specific code behind the DAO pattern, I can easily switch it out. I also love the fact that I can use jMock to test my service layer components without fooling around with actually changing database tables.

Back to automating the process of generating DAOs and their implementation. On one of my projects I am using AppFuse and it comes with a nifty tool to generate a full stack of components for managing persisted data. It can generate the classes either from a POJO or a database table. This is really nice but sometimes I don’t want all of that. I have tables, and therefore pojos, that are not exposed past the service layer so I don’t need the managers, controllers, and views that get generated by this tool. In this case, I really just want the DAO. I could use something like XDoclet, which BTW is what AppFuse’s appgen uses, but Java 1.5 has added a new feature called Generics that I have been having lots of fun figuring out how to use. So here is what I did:

Firstly, I defined what I thought was the basic level of interface that all DAOs should provide. Essentially the CRUD methods:

package com.dhptech.dao;

import java.io.Serializable;
import java.util.List;

/**
 * A generic DAO interface definition.  This interface should be extended
 * even if the new interface will add no additional functions.
 * 
 * @author Dana P'Simer
 *
 * @param <T> The class of the pojo being persisted.
 * @param <I> the class of the pojo's id property.
 */
public interface GenericDao<T,I extends Serializable> {
    /**
     * Get the object with the id specified.
     * 
     * @param id the id of the instance to retrieve.
     * 
     * @return the instance with the given id.
     */
    T get(I id);
    
    /**
     * Get all instances that match the properties that are set in the given 
     * object using a standard Query by Example method.
     *  
     * @param t the example bean
     * 
     * @return a list of beans that match the example.
     */
    List<T> get(T t);
    
    /**
     * Get all instances of this bean that have been persisted.
     * 
     * @return a list of all instances.
     */
    List<T> get();
    
    /**
     * Persist the given bean.
     * 
     * @param t the bean to persist.
     */
    void save(T t);
    
    /**
     * Remove the bean with the given id.
     *
     * @param id the id of the bean to remove.
     */
    void remove(I id);
    
    /**
     * Remove the bean passed.  same as remove(t.<idProoertyGetter>())
     * 
     * @param t the object to remove.
     */
    void remove(T t);
}

Here is the generic implementation:

/**
 * 
 */
package com.dhptech.dao.hibernate;

import java.io.Serializable;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;

import com.dhptech.dao.GenericDao;

/**
 * @author danap
 *
 */
public abstract class GenericDaoHibernate<T,I extends Serializable> implements GenericDao<T,I> {
    /**
     * The hibernate template to use.
     */
    private HibernateTemplate hibernateTemplate;
    
    /**
     * The class of the pojo being persisted.
     */
    private Class<? extends T> clazz;
    
    protected GenericDaoHibernate(Class<? extends T> clazz) {
        this.clazz = clazz;
    }
    
    /**
     * @return the hibernateTemplate
     */
    public HibernateTemplate getHibernateTemplate() {
        return hibernateTemplate;
    }

    /**
     * @param hibernateTemplate the hibernateTemplate to set
     */
    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
        this.hibernateTemplate = hibernateTemplate;
    }

    /**
     * @return the clazz
     */
    protected Class<? extends T> getClazz() {
        return clazz;
    }

    /**
     * @param clazz the clazz to set
     */
    protected void setClazz(Class<? extends T> clazz) {
        this.clazz = clazz;
    }

    /**
     * @see com.dhptech.dao.GenericDao#get(java.io.Serializable)
     */
    @SuppressWarnings("unchecked")
    public T get(I id) {
        return (T) getHibernateTemplate().load(clazz, id);
    }

    /**
     * @see com.dhptech.dao.GenericDao#get(java.lang.Object)
     */
    @SuppressWarnings("unchecked")
    public List<T> get(final T t) {
        if (t == null) {
            return get();
        } else {
            // filter on properties set in the customer
            HibernateCallback callback = new HibernateCallback() {
                public Object doInHibernate(Session session) throws HibernateException {
                    Example ex = Example.create(t).ignoreCase().enableLike(MatchMode.ANYWHERE);
                    return session.createCriteria(clazz).add(ex).list();
                }
            };
            return (List<T>) getHibernateTemplate().execute(callback);
        }
    }

    /**
     * @see com.dhptech.dao.GenericDao#get()
     */
    @SuppressWarnings("unchecked")
    public List<T> get() {
        return getHibernateTemplate().loadAll(clazz);
    }

    /**
     * @see com.dhptech.dao.GenericDao#remove(java.io.Serializable)
     */
    public void remove(I id) {
        remove(get(id));
    }

    /**
     * @see com.dhptech.dao.GenericDao#remove(java.lang.Object)
     */
    public void remove(T t) {
        getHibernateTemplate().delete(t);
    }

    /**
     * @see com.dhptech.dao.GenericDao#save(java.lang.Object)
     */
    public void save(T t) {
        getHibernateTemplate().save(t);
    }
}

You will notice that this class is defined abstract and that it takes a Class<? extends T> object as an agument to the constructor. This is necessary because Generics are designed to assure type safeness through compile time checks. The rule is, if you have no “unchecked” warnings then you are type safe. At runtime there is no class specific information available. so you cannot have an expression like “T.class” to get the class of the parameter. Since Hibernate uses the class to lookup the mapping, for operations like the load function, we need to pass this in here. This also forces a caller to define a concrete class that extends this one in order to define the constructor that will pass in the Class object that is required. While one could get away without defining an subclassing interface for this pattern, I think it is a good practice to define both an interface and implemenation that extend these classes.

It would be simple to reimplement this generic implementation for other persistence models. Not sure if it could be done for a JDBC persistence framework but I know it can for Torque or iBatis.

Now all one has to do to use these is to define the specific DAO’s interface and extend GenericDao, then create the specific DAO’s hibernate implementation by extending the GenericDaoHibernate. Here is an example. Supose you have the following POJO:

package com.dhptech.dao.model;

/**
 * @author danap
 *
 */
public class Customer {
    private Integer id;
    private Integer version;
    private String name;
    private String contactName;
    private String contactTelNum;
    
    /**
     * 
     */
    public Customer() {
        // TODO Auto-generated constructor stub
    }

    /**
     * @return the id
     */
    public Integer getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * @return the version
     */
    public Integer getVersion() {
        return version;
    }

    /**
     * @param version the version to set
     */
    public void setVersion(Integer version) {
        this.version = version;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the contactName
     */
    public String getContactName() {
        return contactName;
    }

    /**
     * @param contactName the contactName to set
     */
    public void setContactName(String contactName) {
        this.contactName = contactName;
    }

    /**
     * @return the contactTelNum
     */
    public String getContactTelNum() {
        return contactTelNum;
    }

    /**
     * @param contactTelNum the contactTelNum to set
     */
    public void setContactTelNum(String contactTelNum) {
        this.contactTelNum = contactTelNum;
    }
}

To create a simple DAO for this object you could define the following interface:

package com.dhptech.dao;

import java.util.List;

import com.dhptech.dao.model.Customer;

/**
 * @author danap
 */
public interface CustomerDao extends GenericDao<Customer,Integer> {
    Customer getCustomerByName(String name);
    List<Customer> searchCustomersByName(String name);
}

Now the hibernate implementation:

package com.dhptech.dao.hibernate;

import java.sql.SQLException;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.orm.hibernate3.HibernateCallback;

import com.dhptech.dao.CustomerDao;
import com.dhptech.dao.model.Customer;

/**
 * @author danap
 *
 */
public class CustomerDaoHibernate extends GenericDaoHibernate<Customer, Integer> implements CustomerDao {
    /**
     * The default constructor.
     */
    public CustomerDaoHibernate() {
        super(Customer.class);
    }

    /**
     * @see com.dhptech.dao.CustomerDao#getCustomerByName(java.lang.String)
     */
    public Customer getCustomerByName(String name) {
        Customer ex = new Customer();
        ex.setName(name);
        List<Customer> matches = get(ex);
        if ( matches.size() == 0 ) {
            throw new DataRetrievalFailureException("No customer found with the name, '"+name+"'");
        } else if ( matches.size() > 1 ) {
            throw new DataRetrievalFailureException("Too many customers found with the name, '"+name+"'");
        } else {
            return matches.get(0);
        }
    }

    /**
     * @see com.dhptech.dao.CustomerDao#searchCustomersByName(java.lang.String)
     */
    @SuppressWarnings("unchecked")
    public List<Customer> searchCustomersByName(final String name) {
        return (List<Customer>) getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                return session
                    .createCriteria(getClazz())
                    .add(Restrictions.like("name", name, MatchMode.ANYWHERE))
                    .list();
            }
        });
    }
}

That is it.. Now I can enjoy the persistence layer independence of the DAO pattern without incurring the additional cost of actually coding one for each of my POJOs.

Addendum – added 10/07/2006

Well it seems I was not the first person to think of this, of course. While I was looking at other implementations, I found that most had followed the basic design I have here. The Class object is passed into the generic base implementation because of the fact that the expression “T.class” is illegal. Well when I was reading hibernate.org’s contribution to this pattern ( see “Hibernate’s Generic DAO Pattern” ) and I saw some code that I thought was very interesting. It seems that it is possible to find the generic type arguments at runtime and get back a Class object for them. Here is the new constructor for the GenericDaoHibernate class:

     /**
      * The class of the pojo being persisted.
      */
     private Class<T> clazz;
     
     protected GenericDaoHibernate() {
          this.clazz = (Class<T>)
               ((ParameterizedType)getClass().getGenericSuperclass())
               .getActualTypeArguments()[0];
     }