This is exactly the problem that Spring's transaction and resource management solves: DAO interfaces that abstract from the implementation technology, and generic transaction demarcation at the business facade level.
Basically, Spring will automatically associated resources like Hibernate Sessions with the current transaction, on first access - no custom ThreadLocals anymore. Business facades simply demarcate transaction, without worrying about those resources, and without being tied to the actual persistence technology.
DAOs simply fetch a resource do something with it, not caring about where it came from: When within a transaction, it will be the transactional resource (i.e. Hibernate Session); else, a new one will implicitly get created. Essentially, DAOs just implement the actual data access operations, via Spring's HibernateTemplate or SessionFactoryUtils.
Spring provides its own transaction demarcations means, but also supports JTA. The Hibernate DAO support classes will automatically adapt to the environment, i.e. associate resources with Spring transactions or JTA transactions. In the case of JTA, all you need to do is configure Hibernate's TransactionManagerLookup accordingly.
So provided that you use JTA, simply use Spring in a library style within your DAOs:
Code:
public class ProductDaoImpl implements ProductDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public List loadProductsByCategory(String category) throws DataAccessException {
HibernateTemplate hibernateTemplate = new HibernateTemplate(this.sessionFactory);
return hibernateTemplate.find("from test.Product product where product.category=?", category);
}
}
Or, if you prefer to work directly on the Hibernate Session:
Code:
public class ProductDaoImpl implements ProductDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public List loadProductsByCategory(String category) throws DataAccessException {
Session session = SessionFactoryUtils.getSession(this.sessionFactory, true);
try {
return session.find("from test.Product product where product.category=?", category, Hibernate.STRING);
}
catch (HibernateException ex) {
throw SessionFactoryUtils.convertHibernateAccessException(ex);
}
finally {
SessionFactoryUtils.closeSessionIfNecessary(this.sessionFactory);
}
}
}
DataAccessException is the root of Spring's unchecked DAO exception hierarchy. HibernateTemplate and SessionFactoryUtils.convertHibernateAccessException will automatically convert HibernateExceptions to appropriate DataAccessException subclasses.
Note that you just use either Spring's HibernateTemplate or SessionFactoryUtils within your DAO implementation; simply fetch the SessionFactory from wherever you hold it. With that usage style, your application does not use Spring for IoC configuration - still you can fully leverage Spring's transaction-scoped resource management!
If you're interested, have a look at the article at
http://www.hibernate.org/110.html for further code-level introduction, and at the Petclinic sample app that comes with the Spring distribution for a working example that uses Spring's Hibernate support (with Spring's own transaction demarcation means).
Juergen