Hi everybody!
We are using Hibernate 2.1 in a multi-tier architecture. It's working absolutely fine, but I'm not yet sure, if my approach on using the ThreadLocalSession-Pattern is absolutely "clean". We access our domain-model through Stateless-SB in JTA, coming from the web-tier (Struts) and sometimes coming from another EJB (triggered by Quartz).
To get control over the transactions, I extended the common ThreadLocal-Pattern to the following code:
Code:
public class ThreadLocalSession
{
//Logging
private static Log log = LogFactory.getLog(ThreadLocalSession.class);
public static final String DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction";
private static final ThreadLocal sessionContext = new ThreadLocal();
private static String jndiname;
private Session session;
private Transaction hiTransaction;
private int level;
static
{
jndiname = CSCPreferences.getPreference("hibernate.jndi.name");
}
/**
* Start a new Session or reuse old one if nested.
* @return
* @throws PersistenceLayerException
*/
public static Session currentSession()
throws PersistenceLayerException
{
ThreadLocalSession tlSession = (ThreadLocalSession)sessionContext.get();
if (tlSession == null)
{
tlSession = new ThreadLocalSession();
SessionFactory sf;
try
{
sf = (SessionFactory) new InitialContext().lookup(jndiname);
} catch (NamingException e)
{
// TODO Auto-generated catch block
throw new RuntimeException(e);
}
try
{
log.debug("||||||||||| Creating new Hibernate-Session for thread |||||||||||||||||");
tlSession.session = sf.openSession();
tlSession.session.setFlushMode(FlushMode.COMMIT);
log.debug("+++++++++++ beginning new Hibernate-Transaction +++++++++++++");
tlSession.hiTransaction = tlSession.session.beginTransaction();
} catch (HibernateException e2)
{
throw new PersistenceLayerException("Error opening session", e2);
}
tlSession.level = 0;
sessionContext.set( tlSession );
} else
{
log.debug("|||||||||||| Re-using Hibernate-Session of thread |||||||||||||||||||||");
}
tlSession.level++;
return tlSession.session;
}
/**
* Try to properly close the underlying Session.
*
*/
public static void closeSession()
{
ThreadLocalSession tlSession = (ThreadLocalSession)sessionContext.get();
if (tlSession == null)
{
return;
}
tlSession.level--;
if (tlSession.level <= 0)
{
//we're last in chain
try
{
if ((tlSession.hiTransaction!=null) && (!tlSession.hiTransaction.wasRolledBack()))
{
//everything looks fine, try to commit
tlSession.hiTransaction.commit();
log.debug("+++++++++++++ Hibernate-Transaction comitted +++++++++++++++");
} else
{
//bad, rollback
try
{
tlSession.hiTransaction.rollback();
log.warn("+++++++++++ rolled back Hibernate-Transaction ++++++++++++");
} catch (Exception e)
{
log.fatal("Errror rolling back User-Transaction", e);
}
}
} catch (HibernateException e)
{
try
{
tlSession.hiTransaction.rollback();
log.warn("+++++++++++ rolled back User-Transaction ++++++++++++");
} catch (Exception e1)
{
log.fatal("Errror rolling back User-Transaction", e1);
}
} finally
{
if (tlSession.session != null && tlSession.session.isOpen())
{
try
{
tlSession.session.close();
log.debug("|||||||||| Hibernate-Session closed ||||||||||||||||");
} catch (HibernateException e2)
{
log.fatal("Error closing Hibernate-Session!", e2);
}
}
}
//reset
sessionContext.set( null );
}
}
/**
* Something bad happened and we need to be told to rollback.
*
*/
public static void cancelTransaction()
{
log.warn("++++++++++ marking HibernateTransaction for rollback ++++++++");
ThreadLocalSession tlSession = (ThreadLocalSession)sessionContext.get();
if (tlSession == null)
{
return;
}
try
{
tlSession.hiTransaction.rollback();
} catch (HibernateException e)
{
log.error("+++++++++++ Couldn't mark transaction for rollback +++++++++++",e);
}
}
}
Thanks a lot already for any of your suggestions! I love my Hibernate :)