Thursday, April 16, 2009

Follow up to the AbstractHibernateDao

In my writing about the AbstractHibernateDao here I mention that you no longer need to extend HibernateDaoSupport class. You do lose one thing though. Your new AbstractHibernateDao based DAO will now throw HibernateExceptions, not Spring DataAccessExceptions. Now, to me, this isn't the end of the world. In a Hibernate 3.2+ world Hibernate does have a clear exception hierarchy. Not like the old days where there was just "HibernateException". That sucked.

If you do want your DAOs to throw Spring DataAccessExceptions there are two simple things to do.
  1. Add the @Repository annotation to your DAO impl.
  2. Add a PersistenceExceptionTranslationPostProcessor bean to your Spring setup.

What this does is tell Spring to wrap an interceptor around your DAO bean that handles the exception translation.

Looking at the UserDaoImpl from the past article...

@Repository
public class UserDaoImpl extends AbstractHibernateDao<User> implements UserDao {

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

Then in your Spring application context.xml you simply add one line...




Now, this pattern I present is somewhat old school. There is all this fancy component scanning stuff you can do with the Spring 2.5 XML namespaces. Honestly the amount of voodoo that goes on there freaks me out a bit. You can study that on your own if you'd like. A good place to start is here.

4 comments:

lumpynose said...

Ray, I can't get this to work as is because it's complaining about a missing no arg constructor. The aop proxy and cglib guys are crashing the party. I think it's because I'm using @Transactional in the abstract class:

@Transactional(readOnly = true)
protected List[E] list(final Criteria criteria) {
@SuppressWarnings("unchecked")
final List[E] result = criteria.list();

if (CollectionUtils.isEmpty(result))
return (Collections.emptyList());

return (result);
}
So I had to resort to setters. I'm not too keen on having to use the @PostConstruct annotation because the jar it's in isn't so easily located.

import javax.annotation.PostConstruct;

import org.hibernate.SessionFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

@Repository
public class UserDao extends AbstractHibernateDao[User] {
private final transient Logger log = LoggerFactory.getLogger(getClass());

/**
*/
@PostConstruct
public void init() {
this.log.debug("called");

super.setEntityClass(User.class);
}

/**
* (non-Javadoc)
*
* @see edu.berkeley.ist.waitlist.db_new.dao.api.AbstractHibernateDao#setSessionFactory(org.hibernate.SessionFactory)
*/
@Override
@Autowired
public void setSessionFactory(final SessionFactory sessionFactory) {
this.log.debug("called");

super.setSessionFactory(sessionFactory);
}
}
Suggestions, ideas?

Ray Krueger said...

Hmm, couple questions...
Do you have the AbstractHibernateDao or its subclasses annotated as Transactional?

Are you forcing the use of cglib here?

lumpynose said...

Sorry, I forgot to spell that out. Correct, the abstract class is using the @Transactional annotation, and I'm thinking now that that's not a good idea. I'm thinking that the annotations should be in the service layer class, not the dao. Does that sound right?

Ray Krueger said...

Yep, that's exactly what I was getting at.