As suggested by Jerry we attacked the more general case by Sub-classing AbstractDAO and using UnitOfWorkRequestDispatcher.java
public class AbstractDao<TYPE> extends AbstractDAO<TYPE> {
public interface UnitOfwork {
public void perform();
}
private final SessionFactory sessionFactory;
public AbstractDao(SessionFactory sessionFactory) {
super(sessionFactory);
this.sessionFactory = sessionFactory;
}
public void perform(UnitOfwork unitOfwork) {
final Session session = sessionFactory.openSession();
if(ManagedSessionContext.hasBind(sessionFactory)) {
throw new IllegalStateException("Already in a unit of work!");
}
try {
configureSession(session);
ManagedSessionContext.bind(session);
session.beginTransaction();
try {
unitOfwork.perform();
commitTransaction(session);
} catch (Exception e) {
rollbackTransaction(session);
this.<RuntimeException>rethrow(e);
}
} finally {
session.close();
ManagedSessionContext.unbind(sessionFactory);
}
}
private void configureSession(Session session) {
session.setDefaultReadOnly(false);
session.setCacheMode(CacheMode.NORMAL);
session.setFlushMode(FlushMode.AUTO);
}
private void rollbackTransaction(Session session) {
final Transaction txn = session.getTransaction();
if (txn != null && txn.isActive()) {
txn.rollback();
}
}
private void commitTransaction(Session session) {
final Transaction txn = session.getTransaction();
if (txn != null && txn.isActive()) {
txn.commit();
}
}
@SuppressWarnings("unchecked")
private <E extends Exception> void rethrow(Exception e) throws E {
throw (E) e;
}
}
You can shorten it in the future using lambda expressions because UnitOfWork is a SAM interface. Also note that the default values for DefaultReadOnly, CacheMode and FlushMode are hardcoded.
I'll leave it as an exercise to the reader to figure out how behavior differs from just calling commit() because if you need multiply units of work you probably know what your doing anyway.