The Generic DAO pattern in Java with Spring 3 and JPA 2.0

One thing that annoys me the most is code duplication. The DAO layer is the perfect candidate for this kind of situation. Often, developers forget about OOP, polymorphism and design patterns and just copy&paste code, change the name of the class and voila, we have a brand “new” BankDao class. I’ll present you how to implement a generic DAO pattern to avoid code duplication and preserve type safety in the same time. Why would you care about type safety and just don’t go use the EntityManager’s generic methods. Well, for various reasons:

  1. You know for sure which entity objects can be persisted
  2. You avoid a lot of explicit casts which are error prone
  3. The code is cleaner and  very easy to follow
  4.  You actually apply OOP principles like inheritance and polymorphism. ;)

The purpose of this article is not to get you familiar with the DAO pattern, but to show you a better way of using it. You can find a complete reference about DAO here: DataAccessobject.

The Generic DAO interface

Let’s get started. First of all this article assumes you are using Spring 3 (although this can be easily adapted to Spring 2.5) and JPA 2.0 in you project and the initial configuration is in place:  you already have a data sources declared, an entity manager factory, etc.  The application is basically up&running.

The foundation of using a Generic DAO is the CRUD operations that you can perform on each entity. And this is the least you can have. Additional generic methods can be defined like: count all objects of a specific type; execute generic queries based on some parameters, etc. You’ll see a sample bellow for countAll().

The core of this pattern will be an interface called, surprisingly, GenericDao and its implementation GenericDaoImpl. This is the code:

package insidecoding.javaee.dao

import java.util.List;
import java.util.Map;

public interface GenericDao< T > {
    /**
     * Method that returns the number of entries from a table that meet some
     * criteria (where clause params)
     *
     * @param params
     *            sql parameters
     * @return the number of records meeting the criteria
     */
    long countAll(Map params);

    T create(T t);

    void delete(Object id);

    T find(Object id);

    T update(T t);   
}

The Generic DAO implementation

package insidecoding.javaee.dao.impl;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import insidecoding.javaee.dao.GenericDao;

public abstract class GenericDaoImpl< T > implements GenericDao< T > {

    @PersistenceContext
    protected EntityManager em;

    private Class< T > type;

    public GenericDaoImpl() {
        Type t = getClass().getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType) t;
        type = (Class) pt.getActualTypeArguments()[0];
    }

    @Override
    public long countAll(final Map params) {

        final StringBuffer queryString = new StringBuffer(
                "SELECT count(o) from ");

        queryString.append(type.getSimpleName()).append(" o ");
        queryString.append(this.getQueryClauses(params, null));

        final Query query = this.em.createQuery(queryString.toString());

        return (Long) query.getSingleResult();

    }

    @Override
    public T create(final T t) {
        this.em.persist(t);
        return t;
    }

    @Override
    public void delete(final Object id) {
        this.em.remove(this.em.getReference(type, id));
    }

    @Override
    public T find(final Object id) {
        return (T) this.em.find(type, id);
    }

    @Override
    public T update(final T t) {
        return this.em.merge(t);    
    }
    private String getQueryClauses(final Map<string, object=""> params,
			final Map<string, object=""> orderParams) {
		final StringBuffer queryString = new StringBuffer();
		if ((params != null) && !params.isEmpty()) {
			queryString.append(" where ");
			for (final Iterator<map.entry<string, object="">> it = params
					.entrySet().iterator(); it.hasNext();) {
				final Map.Entry<string, object=""> entry = it.next();
				if (entry.getValue() instanceof Boolean) {
					queryString.append(entry.getKey()).append(" is ")
							.append(entry.getValue()).append(" ");
				} else {
					if (entry.getValue() instanceof Number) {
						queryString.append(entry.getKey()).append(" = ")
								.append(entry.getValue());
					} else {
						// string equality
						queryString.append(entry.getKey()).append(" = '")
								.append(entry.getValue()).append("'");
					}
				}
				if (it.hasNext()) {
					queryString.append(" and ");
				}
			}
		}
		if ((orderParams != null) && !orderParams.isEmpty()) {
			queryString.append(" order by ");
			for (final Iterator<map.entry<string, object="">> it = orderParams
					.entrySet().iterator(); it.hasNext();) {
				final Map.Entry<string, object=""> entry = it.next();
				queryString.append(entry.getKey()).append(" ");
				if (entry.getValue() != null) {
					queryString.append(entry.getValue());
				}
				if (it.hasNext()) {
					queryString.append(", ");
				}
			}
		}
		return queryString.toString();
	}

}

This class offers CRUD operations for a generic type T. The class is marked as abstract so it can be used only through specific entity DAO implementations. Nothing spectacular about this class, except one thing: the constructor. This is where all the magic will happen. In order to keep things as clean as possible and to avoid AOP and other techniques, we use this hack: we get the actual parametrized type of a concrete class, store it in a Class variable and use it in EntityManager’s methods. Reflection rulz!

Writing concrete DAOs

Writing concrete DAOs is a piece of cake. A step-by-step overview will be as following:

  1. Create a specific entity interface that extends GenericDao.
  2. Create a concrete implementation of the above interface that extends GenericDaoImpl and implements the above interface.

Suppose we have a User entity and we want to create a UserDao. We’ll have:

package insidecoding.javaee.dao;

import insidecoding.javaee.model.User;

public interface UserDao extends GenericDao< User > {
    /**
     * Returns an User object that matches the username given
     *
     * @param username
     * @return
     */
    public User loadUserByUsername(String username);
}

---------------------------------------------------------------

package insidecoding.javaee.dao.impl;

import java.util.List;

import javax.persistence.Query;

import org.springframework.stereotype.Component;

import insidecoding.javaee.dao.UserDao;
import insidecoding.javaee.model.User;

@Component("userDao")
public class UserDaoImpl extends GenericDaoImpl< User > implements UserDao {

    public User loadUserByUsername(String username) {
        Query query = this.em
                .createQuery("select u FROM User u where u.username= :username");
        query.setParameter("username", username);
        List users = query.getResultList();
        if (users != null && users.size() == 1) {
            return users.get(0);
        }
        return null;
    }
}

A concrete entity DAO class will extend GenericDaoImpl<Entity> and add additional methods if needed. If you don’t need additional methods this will be just:

package insidecoding.javaee.dao;

import insidecoding.javaee.model.User;

public interface UserDao extends GenericDao< User > {
}

---------------------------------------------------------------
package insidecoding.javaee.dao.impl;

import org.springframework.stereotype.Component;

import insidecoding.javaee.dao.UserDao;
import insidecoding.javaee.model.User;

@Component("userDao")
public class UserDaoImpl extends GenericDaoImpl< User > implements UserDao {

}

Dead simple. As we used the @Component annotation you don’t even need to do additional Spring configuration. Just inject the UserDao and call the available methods. A typical usage will look like this:

package insidecoding.javaee.service.impl;

import org.springframework.transaction.annotation.Transactional;

public interface UserService {

    @Transactional
    void createUser(String uname, String upwd);
}

---------------------------------------------------------------
package insidecoding.javaee.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import insidecoding.javaee.dao.UserDao;
import insidecoding.javaee.model.User;
import insidecoding.javaee.service.UserService;

@Service("userService")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao dao;

    @Override
    public void createUser(String uname, String upwd) {
        User u = new User();
        u.setUsername(uname);
        u.setPassword(upwd);
        dao.create(u);
    }
}

The UserDao will expect a User object as parameter, just as we intended. Same thing for the rest of the methods. With just a few lines of code (just the class and interface declarations) we’ve achieved a type safety way to interact with our domain model.

If you have Spring configured to user annotations the above code can just be dropped into your project and start using and extending  it immediately.

Prior to Java 5 this was impossible. Now that we have generics it’s a shame not to use its power.

If you have any questions, please leave a comment below.

Recommendations




About these ads

20 thoughts on “The Generic DAO pattern in Java with Spring 3 and JPA 2.0

  1. One change I would make is to add key information to the template. So:

    public interface GenericDao {

    …..

    void delete(K id);

    T find(K id);

    ……

    This allows for strong typing in the child classes.

    • Indeed, this is even more strong typed. The above code is just a basic representation of the concept. Further functionality can be added like named queries, specialized find queries or specific project things.
      Thank you for the interest in this article. :)

  2. It would be interesting for me how it would look without using J2EE Container.

    Also what would be the best solution for integrating NamedQueries in specialized DAOs

    thx for the post

    • You don’t need an Application Server to run this. Tomcat is enough. Do you mean generic named queries? If you refer to regular, entity specific named queries you can do this very simple. In your Entity class use this to declare named queries:

      @NamedQueries({
      @NamedQuery(name="findUserByUsername",
      query="SELECT u FROM USER u WHERE username = :uname")
      })

  3. I already have, for each Entity a set of NamedQueries and i am searching for a nice solution to use them in just one GenericDao, without the specialized interfaces. Your GenericDAO have to be extended even if you have an simple Entity, that has just the CRUD functionality. To make it a little bit harder i don’t use spring, and try to implement simple own transaction managemant.

    • Using EntityManager you can execute any named query from any entity in a GenericDAO. You can just have a template method that executes types of queries:
      public <T> Collection<T> executeFindQuery(Class<T> clazz, String query name, Map params){
      ...
      }

  4. Can U upload the whole project with configuration files please ? I just started to play with these the ersistence with JPA and atm things aren’t working properly so i waoul like to take a look on a working project.

    thx

  5. Is the syntax for getQueryClauses() signature correct?

    private String getQueryClauses(final Map params,
    final Map orderParams)

    Why are the types string and object lowercase, and what is that initialisation object=”” in the generic type??

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s