Thursday, October 9, 2008

Generics and DAOs

Since the coming of Hibernate 3, I have started to refactor a lot of code that I simply didn't need anymore. Among these, my data access objects have always seemed repetitive. Repetition (in my opinion) calls for code refactoring. The hibernate tools project aims at fixing this problem by providing a "Dao Code Generator" which can certainly help. Additionally, other tools out there offer similar solutions, however one must understand how much the JVM will have to use in working with our "dao-centric" application. Can we do better?

With the introduction of Generics in Java 5, I found a good way to make good use of this generic approach to DAO's without breaking my application logic. Consider the following code:


package sample;

public interface GenericDao
<T, PK extends Serializable> {
/**
* Persists the new instance object into the
* persistent store.
* @param newInstance - the instance object
* to persist.
* @return the primary key of the T object.
*/
PK createEntity(T newInstance);

/**
* Retrieve the an object that was previously
* persisted to the persistent store using the
* primary key passed in the parameter.
* @param recnum - the primary key of the
* persisted object.
* @return the object instance.
*/
T getByRecnum(PK recnum);

/**
* Deletes an entity from the persistent store.
* Note that all cascades associated in the
* persistent object (JPA) are applied. If
* orphans are needed, you must brake the
* association first before deleting this
* entity.
* @param recnum - the primary key of the
* entry to delete.
*/
void deleteEntity(PK recnum);
}

This interface defines a generic contract for data access to an object of type T with a primary identifier (primary key, if you will) named PK that is of type Serializable (more on this on some other post). Now, consider the following hibernate implementation:

package sample; 

public class HibernateGenericDaoImpl<T, PK extends Serializable>
      implements GenericDao<T, PK> {
  private HibernateTemplate m_hTemplate;
  private Class<t> m_type;

/**
* Default Constructor for this implementation.
* @param type - the type of DAO to generate
* @param hibernateTemplate - the template to
* use.
*/
public HibernateGenericDaoImpl (Class<T> type,
         HibernateTemplate hibernateTemplate) {
  m_type = type;
  m_hTemplate = hibernateTemplate;
}

/**
* {@inheritDoc}
*/
@Override
public PK createEntity(T newInstance) { 
  return (PK) m_hTemplate.save(newInstance);
}

/**
* {@inheritDoc}
*/
@Override
public T getByRecnum(PK recnum) { 
  return (T) m_hTemplate.get(m_type, recnum);
}

/**
* {@inheritDoc}
*/
@Override
public void deleteEntity(PK recnum) {
  T entity = getByRecnum(recnum);
  if (entity != null) {
    m_hTemplate.delete(entity);
  }
}
}

This implementation of the GenericDao contract uses Spring's HibernateTemplate which is a thin layer on top of Hibernate. It basically uses the template to perform operations on object model T. Of course, you can add more generic methods as needed, but the idea is the same. Possibilities are endless. Now, with the following Spring's configuration:

And the following service code:

package sample;

public class BarService {
private GenericDao<Foo, Long> m_fooDao;

/**
* Default constructor with injected dao
* @param fooDao - the dao to inject.
*/
public BarService(GenericDao<Foo, Long> fooDao) {
m_fooDao = fooDao;
}
}
This means that the Service code can now have an injected dao that is generic. With these generic approach to DAOs, you now avoid writing custom code for each model object (in our case, of type sample.Foo). Now, my strategy for ensuring that my application will not turn into a critical state was to make sure that all our unit-test passed after the code refactoring process had finished. My code coverage percentage certainly decreased as I removed custom implementations of unneded DAO and encourage the use of the generic Dao

Keep in mind that Generics are supported in other languages as well, so this approach can certainly be re-used (if at all).

No comments: