Attending a JBoss Hibernate course a while ago, I noticed how much session management (amongst lots of other things) has matured between Hibernate 2 and 3.
Session management used to be a “client concern”. Hibernate couldn’t help a client managing it’s own sessions, since the how and when of session initialization and clean up differed too much from use case to use case. The standard approach in the tutorial and other learning materials, even for this advanced course, take the form of:
try { session = sessionFactory.openSession(); tx = session.beginTransaction(); // do work tx.commit(); } catch (HibernateException e) { tx.rollback(); } finally { if (session != null) session.close(); }
Although this code leads to the ugliest spaghetti code with strong binding to hibernate imaginable in even the smallest of projects, the motivation seems to be that once an entity is loaded in the session, just traversing associations to other entities or collections will load these as well, thus completely hiding the session from the application code. The necessary “root” objects could be loaded upfront, allowing the application code to works as if all objects always reside in memory. The drawback is that once the application decides that it needs an entity that is not reachable by traversing already loaded objects or needs to save an entity that is not associated to persistent entities, it will need a session. Opening a new session is possible, but working with entities from different sessions leads to severe headaches!
The official solution was to store the session in a ThreadLocal. Although this was the officially recommended solution, there was no support for it in the Hibernate distributions. Each Hibernate user would have to implement this solution over and over again. The same approach as shown before could be used, just storing the session in a ThreadLocal and retrieving it to close it afterwards in a finally block. This approach works rather well, as long as you can bear the code clutter and don’t need any advanced transaction management.
The Hibernate documentation hints at using interception to open and close your sessions, but again there is no support for this. Caution must be exercised when using this approach. When the interceptor is found within the context of an intercepted method, opening a new session could lead to NonUniqueObjectExceptions when crossing the implicit session boundaries. Starting a new transaction will lead to even less defined behavior, since Hibernate will happily try to open another transaction on the underlying JDBC connection and nested transactions are officially not supported by the JEE spec.
When using an interceptor in this fashion, the application should provide the interceptor with enough information to determine what the correct behavior should be. Should it open a new session regardless of an existing session? Should it use the existing transaction, probably fetching the session from a ThreadLocal? Should it bother with transaction management at all or leave this to the application code? Should it close the session after the intercepted method has returned? The Hibernate docs do not provide any insight into this area of problems.
As of Hibernate 3.1 a new strategy is supported. Sessions can be bound to the current transaction context using the getCurrentSession() method on a SessionFactory. In a JTA context this works just perfect and the code above could be simplified to simply:
session = sessionFactory.getCurrentSession(); //do work
The transaction context would already be set by the container managed JTA transaction, so the application code does not have to concern it self with starting and committing or rolling back the transaction. Since Hibernate relies here on the abilities of the JEE container, transactions are demarcated declaratively and transaction suspension is supported.
One of the problems with this approach is that the code won’t work without a JTA container. Fortunately JBoss can be used as a light-weight container even in JUnit tests. If JBoss is not your Application Server of choice, you’re back at where you started, opening and closing sessions as you go.
As I was sitting at the Hibernate course listening to the instructor summing up all these new cool features, I couldn’t help thinking about Spring and how it supports all these features and more. Spring will bind a Hibernate session to the transaction context, as soon as one is requested. It will flush and close the session automatically at transaction commit. It allows for manually opening and closing the session through it’s static SessionFactoryUtils methods, which will check for the transaction bound session. Spring allows declarative transaction demarcation using AOP without depending on a JTA compliant container. The application code can be completely oblivious as to whether it is running in a container or in a JUnit test as long as the transaction manager is configured correctly.
Spring also supports transaction-less environments. When there is no transaction context associated, the HibernateInterceptor will open a new session and close it again after the intercepted method has returned either successfully or by throwing an Exception. Take care when intercepting several methods and sharing objects between these calls. These objects will be detached between the calls, since Spring closes the session eagerly (after flushing it). The normal semantics apply to these detached objects; to be able to use lazy loading or (late) flush at commit, the object needs to be reattached to the new session. session.lock(..) or session.refresh(..) can be used for this. Handling detached objects with Hibernate is a whole world on it’s own though, usually filled with a lot of pain.
In this area Spring really seems to cover all possible use cases and is very easy to apply. I would recommend using Spring for Hibernate session management under all circumstances, regardless of how Hibernate is used in the rest of the application.
Since time was short and even the most obvious use of Hibernate had to be covered in the two day course, I didn’t get to share this sentiment with the instructor and my peers. He’d probably come up with some cunning deep argument as to why using HibernateTemplate in my application would lead to hopelessly broken code and erroneous transaction handling, but that still wouldn’t fulfil my session handling needs.