Thread Local SessionHibernate is designed to be useable in any kind of Java application, including applications that make extensive use of multithreading. So, unlike the ODMG API, Hibernate's native API does not use the current thread to maintain associations between Session and Transaction or between Session and application thread. This can be inconvenient for J2EE applications where access to a Session instance is always from a particular thread. It is particularly inconvenient when using a DAO pattern, when the different DAO classes need to obtain the (same) current session. A common solution to this problem is to keep the Session in a ThreadLocal. The following class shows one possible implementation:
public class HibernateSession {
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession()
throws NamingException, HibernateException {
Session s = (Session) session.get();
if (s == null) {
SessionFactory sf = (SessionFactory) new InitialContext().lookup("SessionFactory");
s = sf.openSession();
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
session.set(null);
if (s != null) s.close();
}
}
For a detailed explanation of ThreadLocal variables, see IBM Developer Works.
Related: Session Handling with the Spring FrameworkThe Spring Framework offers a convenient pre-built implementation of the Thread Local Session pattern, integrated with its transaction management infrastructure via a Hibernate-specific strategy. Additionally, there's special support for Thread Local Sessions in the JTA strategy too, enabling proper cache handling without any container-specific setup. Spring also offers IoC-style templating and AOP solutions for simplified Hibernate Session handling within DAOs, and easy configuration and handling of SessionFactory instances (be it local or from JNDI). Together with similar support for plain JDBC and JDO, this allows for clear application layering with data access and business logic tiers. Regardless of the persistence mechanism, Spring provides the full comfort of high-level transaction management (with or without JTA). This can be combined with a Spring application context that wires up an application's beans, but any part can also be reused individually. Note that Spring adopts a very lightweight approach. You can use a lot of features respectively classes in a library style, as everything is designed as a set of reusable JavaBeans. Don't be discouraged by the fact that Spring can serve as full application framework too. You're invited to review and leverage the Spring approach, no matter to what extent, before deciding to take the effort and risk of building such infrastructure in-house. Data Access with the Spring Framework
Steve Ebersole: Here is a variation on the above ThreadLocalSession. The main difference is that it undersands the concept of nested calls. Note that this usage is meant specifically for use in CMT session beans.
public class ThreadLocalSession
{
private static final ThreadLocal sessionContext = new ThreadLocal();
private Session session;
private int level;
public static Session currentSession()
throws HibernateException
{
SessionFactory factory = ...;
ThreadLocalSession tlSession = (ThreadLocalSession)sessionContext.get();
if (tlSession == null)
{
tlSession = new ThreadLocalSession();
tlSession.session = factory.openSession();
tlSession.level = 0;
sessionContext.set( tlSession );
}
tlSession.level++;
return tlSession.session;
}
public static void closeSession()
throws HibernateException
{
ThreadLocalSession tlSession = (ThreadLocalSession)sessionContext.get();
if (tlSession == null)
{
return;
}
tlSession.level--;
if (tlSession.level <= 0)
{
if (tlSession.session != null && tlSession.session.isOpen())
{
tlSession.session.close();
}
sessionContext.set( null );
}
}
}
Auto timeout SessionChristian: I don't see the problem. You know when the execution of your thread starts, in almost all situations: HTTP request coming in, SOAP request, whatever. You also know in most situation when your thread is finished: A view is completely rendered, a response is send, whatever. The only thing you have to do is: Attach an Observer/Observable (or a ServletFilter!) to your ThreadLocal management, opening and closing the Session when Events (like "View rendered" or "Action complete") are fired. I don't recommend counting references or doing any other "let's just keep that resource open and hope that it works" strategy. Open your Session when starting your thread, close it at the end of the thread. This should be easy in almost any circumstances. The code and approach shown below is not recommended. I'm talking about some timeout mechanism below, not the reference counting above, but yes, both are unneccessary and don't help if your code is too broken to finally close each opened Session. As discussed in forum. There might be the need for a auto timeout session version combined threadlocal. This way we reduced the need to manual track call count and be able to actively close the session.
class HibernateSession {
public static final ThreadLocal session = new ThreadLocal();
Session hibernateSession;
public static Session currentSession() throws HibernateException {
Session s = (Session) session.get();
if (s == null) {
s = factory.openSession();
s = (Session) createSession(s,50 * 1000); //50 sec
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
session.set(null);
if (s != null){
s.flush();
s.close();
}
}
private static Session createSession(final Session hibernate, final long timeout) {
return (Session)Proxy.newProxyInstance(Session.class.getClassLoader(),
new Class[] { Session.class },
new InvocationHandler () {
Session _hibernate = hibernate;
TimerTask t = null;
Timer timer = null;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("close")) {
//cancel the timer;
if (timer != null) {
timer.cancel();
return method.invoke(_hibernate,args);
}
}
renewLease();
return method.invoke(_hibernate,args);
}
private void renewLease() {
System.out.println("renew called");
if (timer != null) {
timer.cancel();
System.out.println("cancel timer:" + timer);
}
timer = new Timer();
t = new TimerTask() {
public void run() {
try {
//run session close....
closeSession();
} catch (HibernateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
timer.schedule(t,timeout);
}
});
}
}
To use it, user just need to call currentSession function. Strictly speaking, you don't need to use threadlocal to achieve auto-timeout session.
|