Monday, September 24, 2007

The Best AbstractHibernateDao Ever

(Follow up to "The Best Generic DAO Interface Ever")

I love the 3rd grade title theme I got going on here. Anyway...

I've seen so many incarnations of an AbstractHibernateDao out there; some are good, some are bad. Myself, I've always gone the AbstractHibernateDao extends HibernateDaoSupport route myself. I'm a huge fan of Spring for the amount of helpful stuff it provides in all areas of "Enterprisey Software Development".

I've done some re-thinking of the Spring Dao concept lately. See, according to Alef Arendsen of Interface21, "start using the Session and/or EntityManager API directly". In other words, stop using the HibernateTemplate, it isn't really useful.

As it turns out in Spring 2.x in combination with Hibernate 3.x (I'd go with no less than 3.2.1), you don't need the HibernateTemplate. If you are using the Spring LocalSessionFactoryBean to configure your Hibernate SessionFactory, the HibernateTemplate just isn't needed. The reason is that the LocalSessionFactoryBean creates a proxy SessionFactory that implements the SessionFactory.getCurrentSession() method appropriately for Spring intercepted classes.

So if you're using Spring Transaction management (via annotations, declarative xml, or what-have-you), you don't need the HibernateTemplate. Now, the one area that the HibernateTemplate does help with is the Exception translation. Hibernate 2.x just threw HibernateException for everything, you were left guessing really what the true problem was. The Spring HibernateTemplate would translate these 'bad' HiberatenExceptions into Spring's DataAccessException hierarchy. This meant that you could easily handle DataIntegrityViolationException vs. IncorrectResultSizeDataAccessException. Well, Hibernate 3.x has it's own exception hierarchy. So you can handle ConstraintViolationException vs. NonUniqueObjectException.

If you want that exception translation, there's some voodoo you can do with an annotation called "@Repository" (There's that repository word I mentioned in my last post). This annotation wraps your Dao (or Repository) with a proxy that will convert the HibernateExceptions to DataAccessExceptions. I don't think it's really that useful to do, so I don't do it...

So, now, if we combine Spring's Handling of the Session for us, with the Hibernate SessionFactory.getCurrentSession() we get a very simple, very clean AbstractHibernateDao to base our Dao's from...


public abstract class AbstractHibernateDao {

private final Class entityClass;
private final SessionFactory sessionFactory;

public AbstractHibernateDao(
Class entityClass,
SessionFactory sessionFactory) {

Assert.notNull(entityClass,
"entityClass must not be null");
Assert.notNull(sessionFactory,
"sessionFactory must not be null");

this.entityClass = entityClass;
this.sessionFactory = sessionFactory;
}

protected Criteria criteria() {
return currentSession().createCriteria(entityClass);
}

protected Query query(String hql) {
return currentSession().createQuery(hql);
}

protected Session currentSession() {
return sessionFactory.getCurrentSession();
}

protected List all() {
return criteria().list();
}

protected Object get(Serializable id) {
return currentSession().get(entityClass, id);
}

public Class getEntityClass() {
return entityClass;
}
}


The purpose of the AbstractHibernateDao above is to take away any work the sub-class Dao might have to do regarding the "Persistent Class" it's responsible for. Meaning that subclasses don't have to pass the Class all the time.

What does it look like in use? Well, let's pretend we have a User entity and a UserDao.

First, our "User" entity...

public class User {
private Long id;
private String username;
private String email;

//getters and setters omitted
}


And now the User Dao or Repository...

public interface UserDao extends Dao {

User findById(Long id);

User findByUsername(String username);

List findByEmail(String email);

List findAll();

void save(User user);

void delete(User user);
}


Now let's implement that UserDao using our AbstractHibernateDao...

public class UserDaoImpl extends AbstractHibernateDao implements UserDao {

public UserDaoImpl(SessionFactory sessionFactory) {
super(User.class, sessionFactory);
}

public User findById(Long id) {
return (User) get(id);
}

public User findByUsername(String username) {
return (User) criteria().add(
Restrictions.eq("username", username)
).uniqueResult();
}

public List findByEmail(String email) {
return query("from User u where u.email = :email")
.setParameter("email", email)
.list();
}

public List findAll() {
return all();
}

public void save(User user) {
currentSession().saveOrUpdate(user);
}

public void delete(User user) {
currentSession().delete(user);
}
}


If you're curious about what the "Dao" interface here looks like, see my previous post "The Best Generic Dao Interface Ever". Or you can skip it and just know that the Dao interface is simply a Marker, there isn't a single method on it.

Understanding this Dao infrastructure has a very low barrier to entry. You don't need to know how the Spring HibernateTemplate interacts with the Hibernate Session to use this. You only need to know how to use the Hibernate Session. There is very little noise in this Dao, it is all directly related to querying Hibernate, as it should be.

This implementation relies on the Hibernate SessionFactory.getCurrentSession() method. When you combine Spring with Hibernate here you get a very elegant solution with no stuff about transactions or any weird abstraction layer in the way. There are whole books around using the Hibernate Session. The Spring HibernateTemplate only gets a few pages dedicated to it in any Spring book. That right there means you spend less time explaining it to people, tell them to "Read the freakin' manual".

Disclaimer: You might notice that there are some generics used in here that will cause "unchecked" warnings. I didn't bother with a lot of generics and the @SuppressWarnings("unchecked") stuff in this example because they can be distracting. To see the generics cleaned up, read "Adding Generics to the AbstractHibernateDao"

5 comments:

Anirudh Vyas said...

Try this :

http://anirudhvyas.com/root/2008/05/31/making-generic-dao-operation-specific/

Regards
Vyas, Anirudh

Anonymous said...

Hello,

I am new to spring/hibernate. I tried to follow this along but had hard time injecting SessionFactory and entity class into UserDaoImpl bean using Spring. Can you please show me the spring config xml file to do that or more complete source code related to this article?

Thanks a lot in advance!
Andrew

Ray Krueger said...

This is a technique that won't make sense unless you know what you're doing with Spring. I suggest studying some tutorials and getting to know Spring and Hibernate.

davidac said...

I know it is 1012 and this was written in 2007, but I am using your abstract hibernate dao pattern successfully largely. The problem I am having now while doing an HSQL join is a LazyInitializationException complaining there is no session. I have got around this previously by marking some of my mapping elements as lazy=false. But I have my set defined in the mapping as lazy = false already and still get this error. Any insight?

Ray Krueger said...

@davidac You have to initialize a hibernate session before you can do anything with collections. Hibernate will not initialize one for you. Setting them lazy=false will make the errors stop but may make performance suck. Lazy=false is going to tell Hibernate, "Whenever you fetch this object, go ahead and fetch all of it's collections too".

I'd recommend leaving the collections set to lazy unless you're certain you'll always use them. You can tell CriteriaQuery and HQL what to fetch and when if you know you'll need those collections in a particular use case.

Unfortunately this is a complicated problem that's very dependent on you're particular application. Definitely do some research on Hibernate sessions, collections, and fetching strategies.

This would be a great start :)
http://docs.jboss.org/hibernate/orm/3.3/reference/...