Simplifying the Data Access Layer with Spring and Java Generics

1. Overview

This is the second of a series of articles about Persistence with Spring. The previous article discussed setting up the persistence layer with Spring 3.1 and Hibernate, without using templates. This article will focus on simplifying the Data Access Layer by using a single, generified DAO, which will result in elegant data access, with no unnecessary clutter. Yes, in Java.

The Persistence with Spring series:

2. The DAO mess

Most production codebases have some kind of DAO layer. Usually the implementation ranges from a raw class with no inheritance to some kind of generified class, but one thing is consistent – there is always more then one. Most likely, there are as many DAOs as there are entities in the system. Also, depending on the level of generics involved, the actual implementations can vary from heavily duplicated code to almost empty, with the bulk of the logic grouped in an abstract class.

2.1. A Generic DAO

Instead of having multiple implementations – one for each entity in the system – a single parametrized DAO can be used in such a way that it still takes full advantage of the type safety provided by generics.

Two implementations of this concept are presented next, one for a Hibernate centric persistence layer and the other focusing on JPA. These implementation are by no means complete – only some data access methods are included, but they can be easily be made more thorough.

2.2. The Abstract Hibernate DAO

public abstract class AbstractHibernateDAO< T extends Serializable > {
   
   private Class< T > clazz;
   
   @Autowired
   SessionFactory sessionFactory;
   
   public void setClazz( Class< T > clazzToSet ){
      this.clazz = clazzToSet;
   }
   
   public T findOne( Long id ){
      return (T) this.getCurrentSession().get( this.clazz, id );
   }
   public List< T > findAll(){
      return this.getCurrentSession()
       .createQuery( "from " + this.clazz.getName() ).list();
   }
   
   public void save( T entity ){
      this.getCurrentSession().persist( entity );
   }
   
   public void update( T entity ){
      this.getCurrentSession().merge( entity );
   }
   
   public void delete( T entity ){
      this.getCurrentSession().delete( entity );
   }
   public void deleteById( Long entityId ){
      T entity = this.getById( entityId );
      
      this.delete( entity );
   }
   
   protected Session getCurrentSession(){
      return this.sessionFactory.getCurrentSession();
   }
}

The DAO uses the Hibernate API directly, without relying on any Spring templates (such as HibernateTemplate). Using of templates, as well as management of the SessionFactory which is autowired in the DAO were covered in the previous post of the series.

2.3. The Abstract JPA DAO

public abstract class AbstractJpaDAO< T extends Serializable > {
   
   private Class< T > clazz;
   
   @PersistenceContext
   EntityManager entityManager;
   
   public void setClazz( Class< T > clazzToSet ){
      this.clazz = clazzToSet;
   }
   
   public T findOne( Long id ){
      return this.entityManager.find( this.clazz, id );
   }
   public List< T > findAll(){
      return this.entityManager.createQuery( "from " + this.clazz.getName() )
       .getResultList();
   }
   
   public void save( T entity ){
      this.entityManager.persist( entity );
   }
   
   public void update( T entity ){
      this.entityManager.merge( entity );
   }
   
   public void delete( T entity ){
      this.entityManager.remove( entity );
   }
   public void deleteById( Long entityId ){
      T entity = this.getById( entityId );
      
      this.delete( entity );
   }
}

Similar to the Hibernate DAO implementation, the Java Persistence API is used here directly, again not relying on the now deprecated Spring JpaTemplate.

2.4. The Generic DAO

Now, the actual implementation of the generic DAO is as simple as it can be – it contains no logic. Its only purpose is to be injected by the Spring container in a service layer (or in whatever other type of client of the Data Access Layer):

@Repository
@Scope( BeanDefinition.SCOPE_PROTOTYPE )
public class GenericJpaDAO< T extends Serializable >
 extends AbstractJpaDAO< T > implements IGenericDAO< T >{
   //
}

@Repository
@Scope( BeanDefinition.SCOPE_PROTOTYPE )
public class GenericHibernateDAO< T extends Serializable >
 extends AbstractHibernateDAO< T > implements IGenericDAO< T >{
   //
}

First, note that the generic implementation is itself parametrized – allowing the client to choose the correct parameter in a case by case basis. This will mean that the clients gets all the benefits of type safety without needing to create multiple artifacts for each entity.

Second, notice the prototype scope of these generic DAO implementation. Using this scope means that the Spring container will create a new instance of the DAO each time it is requested (including on autowiring). That will allow a service to use multiple DAOs with different parameters for different entities, as needed.

The reason this scope is so important is due to the way Spring initializes beans in the container. Leaving the generic DAO without a scope would mean using the default singleton scope, which would lead to a single instance of the DAO living in the container. That would obviously be majorly restrictive for any kind of more complex scenario.

3. The Service

There is now a single DAO to be injected by Spring; also, the Class needs to be specified:

@Service
class FooService implements IFooService{
   
   IGenericDAO< Foo > dao;
   
   @Autowired
   public void setDao( IGenericDAO< Foo > daoToSet ){
      this.dao = daoToSet;
      this.dao.setClazz( Foo.class );
   }
   
   // ...
   
}

Spring autowires the new DAO insteince using setter injection so that the implementation can be customized with the Class object. After this point, the DAO is fully parametrized and ready to be used by the service.

4. Conclusion

This article discussed the simplification of the Data Access Layer by providing a single, reusable implementation of a generic DAO. This implementation was presented in both a Hibernate and a JPA based environment. The result is a streamlined persistence layer, with no unnecessary clutter.

For a step by step introduction about setting up the Spring context using Java based configuration and the basic Maven pom for the project, see this article. The next article of the Persistence with Spring series will focus on setting up the DAL layer with Spring 3.1 and JPA. In the meantime, you can check out the full implementation in the github project.

If you read this far, you should follow me on twitter here.

 

 

 

 

Top