Implementing Hibernate DAOs

Christian Bauer at the Hibernate team blog has written an improved version of the DAOs of the Caveat Emptor application of the Hibernate in Action book. This new versions will probably be included in the second edition of the book. The new DAOs are implemented in Java 5 using generics and other Java 5 features. Christian Bauer has created a common abstract DAO class, the GenericHibernateDAO, that makes implementing new DAOs easier and removes the code duplication found in the previous versions of the DAOs. I believe that if you plan to use Hibernate with Java 5 you should read his post since it provides a very good starting point for implementing DAOs with generics.

If you are using Hibernate in Java 2 and you want to reuse code from the current version of the CaveatEmptor application you can make a small refactoring to the DAO classes and introduce a common abstract DAO class that will make implementing DAOs easier and will remove much of the duplicate code found in the current implementation of the DAOs.

This refactoring is based on the observation that a lot of the Hibernate operations take the java.lang.Class object of the mapped class as an argument. By introducing a java.lang.Class field (mappedClass) in an abstract DAO class (AbstractDAO) we can move there all the operations of the concrete DAOs of theCaveat Emptor application. With this refactoring of course we assume that we will have a one-to-one mapping between our mapped classes and our DAO classes.

Below I have listed a possible implementation of the abstract DAO and also an implementation of the UserDAO so you can see the code that is removed from the concrete DAOs. The below code is heavily based on the original implementation of Christian Bauer.

// The below code is based on the DAO implementation of
// the Caveat Emptor application of the Hibernate project
// see: http://caveatemptor.hibernate.org
import java.io.Serializable;
import java.util.Collection;

import net.sf.hibernate.Criteria;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.LockMode;
import net.sf.hibernate.Session;
import net.sf.hibernate.expression.Example;

import org.hibernate.auction.exceptions.InfrastructureException;
import org.hibernate.auction.persistence.HibernateUtil;

public class AbstractDAO {

  private Class mappedClass;

  public AbstractDAO(Class c) {
    mappedClass = c;
    HibernateUtil.beginTransaction();
  }

  protected Object getById(Long id, boolean lock) throws InfrastructureException {
    Session session = HibernateUtil.getSession();
    Object object = null;
    try {
      if (lock) {
        object = session.load(mappedClass, id, LockMode.UPGRADE);
      } else {
        object = session.load(mappedClass, id);
      }
    }  catch (HibernateException ex) {
      throw new InfrastructureException(ex);
    }
    return object;
  }

  public Collection findAll() throws InfrastructureException {
    Collection objects;
    try {
      objects = HibernateUtil.getSession().createCriteria(mappedClass).list();
    } catch (HibernateException ex) {
      throw new InfrastructureException(ex);
    }
    return objects;
  }

  public Collection findByExample(Object example) throws InfrastructureException {
    Collection users;
    try {
      Criteria crit = HibernateUtil.getSession().createCriteria(mappedClass);
      users = crit.add(Example.create(example)).list();
    } catch (HibernateException ex) {
      throw new InfrastructureException(ex);
    }
    return users;
  }

  public void makePersistent(Object o) throws InfrastructureException {
    try {
      HibernateUtil.getSession().saveOrUpdate(o);
    } catch (HibernateException ex) {
      throw new InfrastructureException(ex);
    }
  }

  public void makeTransient(Object o) throws InfrastructureException {
    try {
      HibernateUtil.getSession().delete(o);
    } catch (HibernateException ex) {
      throw new InfrastructureException(ex);
    }
  }

  public Class getMappedClass() {
    return mappedClass;
  }
}


import org.hibernate.auction.exceptions.InfrastructureException;
import org.hibernate.auction.model.User;

public class UserDAO extends AbstractDAO {

  public UserDAO() {
    // Pass the class of the DAO to the parent
    super(User.class);
  }

  // Provide a type safe way to get a User object
  // without a cast from the user of the api.
  public User getUserById(Long id, boolean lock) throws InfrastructureException {
    return (User) getById(id, lock);
  }
}

Query by Example and refactoring

I am working on an project to expand an existing application with a large set of new features. Some of these features require domain model changes. As these changes happen we are also refactoring the existing model to better match the new business requirements.

In this application we use Hibernate for the object persistence. As we refactor we sometimes rename javabean properties in our domain model using Eclipse automated renaming feature. This works fine for Java code but breaks the HQL queries which we have to modify manually. We use XDoclet for the mapping metadata so we do not have to manually edit the XML mapping files since they are generated.

One good way to get rid of the pain to have to modify your HQL when you change your domain model is not to use HQL but criteria queries with query by example(QBE).

In query by example you create an example object, an object that looks like the objects you want to fetch with the query. This object is then used by Hibernate to generate the SQL query for you. Let’s illustrate this with a small example.

User exampleUser = new User();
exampleUser.setFirstName(“Spiros”);
Example e = Example.create(exampleUser);
Criteria c = session.createCriteria(User.class);
c.add(e);
c.list();

The above code will retrieve all the User objects with firstName equals “Spiros”.

Please note that example queries can also work for associations and not only for properties.

From the above example it is clear that we have not used any form of query language or special object to express our query. We have used just an instance of the class whose objects we want to retrieve. When we rename a property using a modern IDE the above query will continue to work.

Example queries are not a silver bullet. HQL and Criteria Queries (with criteria expressions not only examples) provide more powerful ways to query your relational database but example queries for simple queries are just fine and allow easier domain object changes.

For more info on Hibernate QBE see here.