Monday, October 13, 2008

Hibernate Session and Transaction Utilizing ThreadLocal Variables

At long last here is the post I promised that uses ThreadLocal variables to handle Hibernate sessions and transactions. The rather long helper class contains methods to obtain the current session (if one doesn't already exist it creates one), close the session, obtain a transaction, commit the transaction and rollback.
This is a good demonstration of the use of ThreadLocal variables and presents workable code that I hope you can use everyday. There is a lot more that can be added to this helper but that's a reader exercise.

public final class HibernateHelper
{
private static final ThreadLocal threadSession =
new ThreadLocal();
private static final ThreadLocal threadTransaction =
new ThreadLocal();
private static SessionFactory mSessionFactory;

static {
mSessionFactory = ServiceLocator.getInstance().getSessionFactory();
}

public static Session getCurrentSession() {
Session s = threadSession.get();
try {
if (s == null || !s.isOpen()) {
if (mLogger.isInfoEnabled()) {
mLogger.info("Opening new Session for this thread.");
}
s = mSessionFactory.openSession();
threadSession.set(s);
}
else {
if (mLogger.isInfoEnabled()) {
mLogger.info("Using current session in this thread.");
}
}
}
catch (HibernateException ex) {
throw ("unable to open hibernate session");
}
return s;
}

public static void closeSession() {
try {
final Session s = threadSession.get();
if (s != null && s.isOpen()) {
mLogger.info("Closing Session of this thread.");
s.close();
}
}
catch (HibernateException ex) {
Throw.loggable(HBM_CLOSE_SESSION, ex, false);
}
finally {
threadSession.set(null);
}
}

public static void beginTransaction() {
Transaction tx = threadTransaction.get();
try {
if (tx != null && !tx.isActive()) {
tx = null;
threadTransaction.set(null);
}
if (tx == null) {
if (mLogger.isInfoEnabled()) {
mLogger.info("Starting new database transaction in this thread.");
}
if (threadSession.get() != null && threadSession.get().isOpen()) {
threadSession.get().close();
threadSession.set(null);
}
tx = getCurrentSession().beginTransaction();
threadTransaction.set(tx);
}
else {
if (mLogger.isInfoEnabled()) {
mLogger.info("Using current database transaction in this thread.");
mLogger.info("Opening new Session for this thread.");
}
}
}
catch (HibernateException ex) {
Throw.loggable(HBM_BEGIN_TRANSACTION, ex, false);
}
finally {
if (threadSession.get() == null || !threadSession.get().isOpen()) {
getCurrentSession();
}
else {
threadSession.get().clear();
}
}
}

public static void commitTransaction() {
final Transaction tx = threadTransaction.get();
try {
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack())
{
Session s = getCurrentSession();
s.flush();
if (mLogger.isInfoEnabled()) {
mLogger.info("Flushing session and committing transaction of this thread.");
}
tx.commit();
}
}
catch (HibernateException ex) {
rollbackTransaction();
Throw.loggable(HBM_COMMIT_TRANSACTION, ex, false);
}
finally {
threadTransaction.set(null);
closeSession();
}
}

public static void rollbackTransaction() {
final Transaction tx = threadTransaction.get();
try {
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) {
mLogger.info("Trying to rollback database transaction of this thread.");
tx.rollback();
}
}
catch (HibernateException ex) {
Throw.loggable(HBM_ROLLBACK_TRANSACTION, ex, false);
}
finally {
threadTransaction.set(null);
closeSession();
}
}
}

Next installment I want to continue our ThreadLocal variable discussion and problems you can run into when using them.

1 comment:

Danut said...

Thank you very much for this post! It's a great resource!