This page explains common techniques to deal with the Session and transactions in Hibernate applications. Refer to the Hibernate reference documentation and the "Transactions and Concurrency" chapter for more information. This page describes Hibernate 3.1.x and code shown here does not work in older versions.
- Unit of Work
- Transactions
- The scope of a unit of work
- Transaction demarcation with JTA
- Transaction demarcation with plain JDBC
- Transaction demarcation with EJB/CMT
- Custom transaction interceptors
- Implementing long Conversations
- Implementing data access objects (DAOs)
- What about the SessionFactory?
- This is all very difficult, can't this be done easier?
Unit of Work
A particular unit of work is grouping data access operations. We usually refer to the Hibernate Session as a unit of work because the scope of a Session is exactly that, in almost all cases. (The Session is also many other things, for example, a cache and a primary API.) To begin a unit of work you open a Session. To end a unit of work you close a Session. Usually you also flush a Session at the end of a unit of work to execute the SQL DML operations (UPDATE, INSERT, DELETE) that synchronize the in-memory Session state with the database.
A Session executes also SQL queries, whenever the developer triggers a query with the API or through loading on demand (lazy loading). Alternatively, think of the Session as a gateway to your database, a map of managed entity instances that are automatically dirty checked, and a queue of SQL DML statements that are created and flushed by Hibernate automatically.
Transactions
Transactions also group data access operations, in fact, every SQL statement, be it queries or DML, has to execute inside a database transaction. There can be no communication with a database outside of a database transaction. (Note that there are such things as read-only transactions, that can be used to improve cleanup time in a database engine if it is not smart enough to optimize its own operations.)
One approach is the auto-commit mode, where every single SQL statement is wrapped in a very short transaction. This mode is never appropriate for an application, but only for ad-hoc execution of SQL with an operator console. Hibernate disables or expects the environment (in J2EE/JEE) to disable auto-commit mode, as applications are not executing ad-hoc SQL but a planned sequence of statements. (There are ways to enable auto-commit behavior in Hibernate but it is by definition slower than regular transactions and less safe. If you want to know more about auto-commit mode, read this.)
The right approach is to define clear transaction boundaries in your application by beginning and committing transactions either programmatically, or if you have the machinery to do this, declaratively (e.g. on service/command methods). If an exception occurs the transaction has to be rolled back (or declaratively, is rolled back).
The scope of a unit of work
A single Hibernate Session might have the same scope as a single database transaction.
This is the most common programming model used for the session-per-request implementation pattern. A single Session and a single database transaction implement the processing of a particular request event (for example, a Http request in a web application). Do never use the session-per-operation anti-pattern! (There are extremely rare exceptions when session-per-operation might be appropriate, you will not encounter these if you are just learning Hibernate.)
Another programming model is that of long Conversations, e.g. an application that implements a multi-step dialog, for example a wizard dialog, to interact with the user in several request/response cycles.
One way to implement this is the session-per-request-with-detached-objects pattern. Once persistent objects are considered detached during user think-time and have to be reattached to a new Session after they have been modified.
The session-per-conversation pattern is however recommended. In this case a single Session has a bigger scope than a single database transaction and it might span several database transactions. Each request event is processed in a single database transaction, but flushing of the Session would be delayed until the end of the conversation and the last database transaction, to make the conversation atomic. The Session is held in disconnected state, with no open database connection, during user think-time. Hibernate's automatic optimistic concurrency control (with versioning) is used to provide conversation isolation.
Hibernate supports several convenience APIs that make implementation of all transaction and conversation strategies easier, with any transaction processing system you might deploy on.
Transaction demarcation with JTA
Hibernate works in any environment that uses JTA, in fact, we recommend to use JTA whenever possible as it is the standard Java transaction interface. You get JTA built-in with all J2EE/JEE application servers, and each Datasource you use in such a container is automatically handled by a JTA TransactionManager. But this is not the only way to get JTA, you can use a standalone implementation (e.g. JOTM) in any plain JSE environment. Another example is JBoss Seam, it comes bundled with a demo application that uses an embeddable version of the JBoss JCA/JTA/JNDI services, hence provides JTA in any deployment situation.
Hibernate can automatically bind the "current" Session to the current JTA transaction. This enables an easy implementation of the session-per-request strategy with the getCurrentSession() method on your SessionFactory:
try { UserTransaction tx = (UserTransaction)new InitialContext() .lookup("java:comp/UserTransaction"); tx.begin(); // Do some work factory.getCurrentSession().load(...); factory.getCurrentSession().persist(...); tx.commit(); } catch (RuntimeException e) { tx.rollback(); throw e; // or display error message }
The advantage of the built-in support should become clear as soon as you write non-trivial applications: you can separate the transaction demarcation code from your data access code. The "current session" refers to a Hibernate Session bound by Hibernate behind the scenes, to the transaction scope. A Session is opened when getCurrentSession() is called for the first time and closed when the transaction ends. It is also flushed automatically before the transaction commits. You can call getCurrentSession() as often and anywhere you want as long as the transaction runs. To enable this strategy in your Hibernate configuration:
- set hibernate.transaction.manager_lookup_class to a lookup strategy for your JEE container
- set hibernate.transaction.factory_class to org.hibernate.transaction.JTATransactionFactory
See the Hibernate reference documentation for more configuration details.
This does not mean that all Hibernate Sessions are closed when a transaction is committed! Only the Session that you obtained with sf.getCurrentSession() is flushed and closed automatically. If you decide to use sf.openSession() and manage the Session yourself, you have to flush() and close() it. So a less convenient alternative, without any "current" Session, is this:
UserTransaction tx = (UserTransaction)new InitialContext() .lookup("java:comp/UserTransaction"); Session session = factory.openSession(); try { tx.begin(); // Do some work session.load(...); session.persist(...); session.flush(); tx.commit(); } catch (RuntimeException e) { tx.rollback(); throw e; // or display error message } finally { session.close(); }
If you manage the Session yourself, code is more difficult to layer. You can't easily move data access operations into a different layer than transaction and Session demarcation.
Transaction demarcation with plain JDBC
If you don't have JTA and don't want to deploy it along with your application, you will usually have to fall back to JDBC transaction demarcation. Instead of calling the JDBC API you better use Hibernate's Transaction and the built-in session-per-request functionality:
try { factory.getCurrentSession().beginTransaction(); // Do some work factory.getCurrentSession().load(...); factory.getCurrentSession().persist(...); factory.getCurrentSession().getTransaction().commit(); } catch (RuntimeException e) { factory.getCurrentSession().getTransaction().rollback(); throw e; // or display error message }
Because Hibernate can't bind the "current session" to a transaction, as it does in a JTA environment, it binds it to the current Java thread. It is opened when getCurrentSession() is called for the first time, but in a "proxied" state that doesn't allow you to do anything except start a transaction. When the transaction ends, either through commit or roll back, the "current" Session is closed automatically. The next call to getCurrentSession() starts a new proxied Session, and so on. In other words, the session is bound to the thread behind the scenes, but scoped to a transaction, just like in a JTA environment. This thread-bound strategy works in every JSE application - note that you should use JTA and a transaction-bound strategy in a JEE environment (or install JTA with your JSE application, this is a modular service).
To enable the thread-bound strategy in your Hibernate configuration:
- set hibernate.transaction.factory_class to org.hibernate.transaction.JDBCTransactionFactory
- set hibernate.current_session_context_class to thread
This does not mean that all Hibernate Sessions are closed when a transaction is committed! Only the Session that you obtained with sf.getCurrentSession() is flushed and closed automatically. If you decide to use sf.openSession() and manage the Session yourself, you have to close() it. So a less convenient alternative, without any "current" Session, is this:
Session session = factory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); // Do some work session.load(...); session.persist(...); tx.commit(); // Flush happens automatically } catch (RuntimeException e) { tx.rollback(); throw e; // or display error message } finally { session.close(); }
If you manage the Session yourself, code is more difficult to layer. You can't easily move data access operations into a different layer than transaction and Session demarcation.
Transaction demarcation with EJB/CMT
Our goal really is to remove any transaction demarcation code from the data access code:
@TransactionAttribute(TransactionAttributeType.REQUIRED) public void doSomeWork() { // Do some work factory.getCurrentSession().load(...); factory.getCurrentSession().persist(...); }
Instead of coding the begin, commit, and rollback of your transactions into your application you could use a declarative approach. For example, you might declare that some of your service or command methods require a database transaction to be started when they are called. The transaction ends when the method returns; if an exception is thrown, the transaction will be rolled back. The Hibernate "current" Session has the some scope as the transaction (flushed and closed at commit) and is internally also bound to the transaction. It propagates into all components that are called in one transactions.
Declarative transaction demarcation is a standard feature of EJB, also known as container-managed transactions (CMT). In EJB 2.x you would use XML deployment descriptors to create your transaction assembly. In EJB 3.x you can use JDK 5.0 annotation metadata directly in your source code, a much less verbose approach. To enable CMT transaction demarcation for EJBs in Hibernate configuration:
- set hibernate.transaction.manager_lookup_class to a lookup strategy for your JEE container
- set hibernate.transaction.factory_class to org.hibernate.transaction.CMTTransactionFactory
Custom transaction interceptors
To remove transaction demarcation from your data access code you might want to write your own interceptor that can begin and end a transaction programmatically (or even declaratively). This is a lot easier than it sounds, after all, you only have to move three methods into a different piece of code that runs every time a request has to be processed. Of course more sophisticated solutions would also need to handle transaction propagation, e.g. if one service method calls another one. Typical interceptors are a servlet filter, or an AOP interceptor that can be applied to any Java method or class.
For an implementation with a servlet filter see Open Session in View.
For an implementation with JBoss AOP see Session handling with AOP.
Implementing long Conversations
If you'd like to design your application with a session-per-conversation strategy, you need to manage the "current" Session yourself. An example with a servlet filter is shown with the Open Session in View pattern.
Implementing data access objects (DAOs)
Writing DAOs that call Hibernate is incredibly easy and trivial. You don't need a framework. You don't need to extend some "DAOSupport" superclass from a proprietary library. All you need to do is keep your transaction demarcation (begin and commit) as well as any Session handling code outside of the DAO implementation. For example, a ProductDAO class has a setCurrentSession() method or constructor, or it looks up the "current" Hibernate Session internally. Where this current Session comes from is not the responsibility of the DAO! How a transaction begins and ends is not the responsibility of the DAO! All the data access object does is use the current Session to execute some persistence and query operations. For a pattern that follows these rules, see Generic Data Access Objects.
What about the SessionFactory?
In the examples above you can see access to the SessionFactory. How do you get access to the factory everywhere in your code? Again, if you run in a JEE environment, or use an embedded service in JSE, you could simply look it up from JNDI, where Hibernate can bind it on startup. Another solution is to keep it in a global static singleton after startup. You can in fact solve both the problem of SessionFactory lookup and Hibernate startup with the same piece of code, a trivial helper class (this is from the tutorial in chapter 1, Hibernate reference documentation):
public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { // Create the SessionFactory from hibernate.cfg.xml sessionFactory = new Configuration().configure().buildSessionFactory(); } catch (Throwable ex) { // Make sure you log the exception, as it might be swallowed System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } }
A more sophisticated version if HibernateUtil that can also switch automatically between JNDI and static singleton can be found in the CaveatEmptor demo application.
Note: There are many variations of much more complex HibernateUtil classes floating around the net. However, for Hibernate 3.1, the code shown above is the only code that is needed. If you use JNDI you might want to have a look at HibernateUtil in the latest CaveatEmptor. Every other HibernateUtil is obsolete for Hibernate 3.1.
This is all very difficult, can't this be done easier?
Hibernate can only do so much as a persistence service, managing the persistence service is however the responsibility of the application infrastructure, or framework. The EJB3 programming model makes transaction and persistence context management very easy, use the Hibernate EntityManager to get this API. Either run your EJBs inside a full J2EE application server (previews available from several vendors) or in a lightweight embeddable EJB3 container, JBoss Embeddable EJB3, in any Java environment. The JBoss Seam framework has built-in support for automatic context management, including persistence and conversations, with only a few annotations in your source code.
Comments