Sunday, August 15, 2010

Hibernate: OpenSessionInView reloaded

In the previous post regarding Hibernate, I've shown how the OpenSessionInView pattern can be implemented.
In short, the idea was that the transactional statements (CRUD operations with the database) happen in between the following lines:

sf.getCurrentSession().beginTransaction();
....
sf.getCurrentSession().getTransaction().commit();

These lines are executed by a servlet filter, making thus sure that the Hibernate session is kept open until the view is rendered, and thus allowing us to use managed entities, as opposed to detached.

However, there is still something about which we haven't talked yet: what happens in the real case, with several simultaneous requests to the servlet ? We know that servlets are multithreaded objects, i.e. multiple threads may call the servlet's methods at the same time. We also know that servlets are not thread safe, so we  have to pay attention to instance or class variables, which can be shared by several threads.

In the case of the OpenSessionPerView pattern, note that sf.getCurrentSession().beginTransaction() may be called by several threads. This means that the result of sf.getCurrentSession(), which is the current Session object, is shared among threads, which is dangerous since a Session is not a thread safe object. This asks for trouble, since the instance of Session is used to create queries and perform CRUD operations with the database.

So how can we ensure a thread-safe behavior of the OpenSessionPerView pattern ? ThreadLocal pattern to the rescue.
In a few words, what we need is a Session object per thread, which can be easily implemented using a ThreadLocal variable, as in the code below:

public class HibernateUtil {
    private static final SessionFactory sessionFactory;
    private static final ThreadLocal threadSession =
    new ThreadLocal();

    static {
      // initialize SessionFactory
      sessionFactory = new Configuration().configure().
      buildSessionFactory();
    }

    public static Session getSession() {
      Session s = (Session) threadSession.get();
      // open a new Session, if this thread has none yet
      try {
        if (s == null) {
          s = sessionFactory.openSession();
          threadSession.set(s);
        }
      } catch (HibernateException ex) {
        throw new InfrastructureException(ex);
      }
      return s;
    }

    public static void closeSession() {
      try {
        Session s = (Session) threadSession.get();
        threadSession.set(null);
        if (s != null && s.isOpen()) s.close();
      } catch (HibernateException ex) {
        throw new InfrastructureException(ex);
      }
    }
}

You get the picture. We would call HibernateUtil.getSession().getTransaction() in our servlet filter, instead of sf.getCurrentSession().beginTransaction(). Now each thread running through the servlet (and the filter) gets its own session object, so it performs the CRUD operations to the database without interfering with the other threads.

6 comments:

  1. Alex asunduko@yahoo.comJune 24, 2011 at 5:43 PM

    I don't see anything wrong with your code, however I am not clear why you seem to argue that SessionFactory.getCurrentSession() can return a session already being used by another thread. According to the hibernate folks, the SessionFacotry is a threadsafe object.
    http://community.jboss.org/wiki/OpenSessionInView#Using_an_interceptor

    ReplyDelete
  2. Wanted to elaborate just a bit more.

    we can do

    org.hibernate.context.ThreadLocalSessionContext

    or

    thread

    both of these will do exactly the same thing you are doing. I wonder what the default is if you don't specify the property. Is the default thread safe? It should be because they promise that Sessionfactory is, but I don't know for sure.

    The other alternative, especially if you we transactions is jta




    or

    ReplyDelete
  3. Hi Alex and sorry for my delayed answer.
    SessionFactor is indeed thread-safe, but here the issue is that in principle two threads could run through the filter I wrote in the previous post:
    http://sorincalex.blogspot.com/2010/06/hibernate-opensessioninview-explained.html
    This means they will both do: sf.getCurrentSession().beginTransaction(), thus they will in fact retrieve THE SAME Session instance (the current session). This could result in inconsistencies in accessing the DB, e.g. if they both try to update the same DB record. Or potentially you could get a Hibernate exception saying that a transaction is already begun with that session.
    So what we need is a different Session instance per thread. This means in fact two distinct transactions for the RDBMS, which will know how to deal with them.

    Greetings,
    Sorin

    ReplyDelete
  4. Alex asunduko@yahoo.comSeptember 20, 2011 at 3:17 PM

    Hi Sorin, thank you for your reply. I do understand the scenario which you are describing because it is all about writing classes which will behave admirably when several threads try to access them simultaneously. But!! If a class - any class, HibernateUtil etc. - has an instance variable which is itself thread safe, then the state of one thread will not be able to mess things up for another.
    In fact what you are saying is that the hibernate folks wrote their session factory incorrectly and it is possible to call getSession() and get a session for one thread of execution which is already being used by another. I would like to see a test case where in fact you were able to replicate such a behavior. As far as I know what happens behind the scenes if you use "org.hibernate.context.ThreadLocalSessionContext:current sessions are tracked by thread of execution." is exactly what you are doing. That is a new "hibernate session" is tied to each new thread of execution which in the web is equivalent to starting a new session for every new browser request. I copied the quote from
    http://docs.jboss.org/hibernate/core/3.3/reference/en/html/architecture.html#architecture-current-session

    Thanks Sorin, hope to keep the J2EE conversation going !!

    ReplyDelete
  5. Hi,
    Sorry again for my late answer.
    The point is that you don't want to mix threads with sessions. You absolutely need to either ensure that you use the same session in several threads in a thread-safe way, or use a local session instance per thread. The latter is what I describe here.
    The problem stems from the fact that transactions are tied to the current thread. So running transactions on different threads from the same session object can lead to unpredictable states in the DB.

    The session factory is thread-safe on purpose, e.g. you know you can use it as an instance variable in a servlet. But the rest is you business, e.g. you need to ensure proper usage of the session.
    An alternative would be to always open a new session, e.g. sessionFactory.openSession(). While this works fine with multithreading, it creates needlessly so many resources. Again, the thread local pattern comes to rescue, since it limits the number of session objects (i.e., each thread uses exactly one).

    Greetings,
    Sorin

    ReplyDelete