11.2. Database transaction demarcation

Datatabase (or system) transaction boundaries are always necessary. No communication with the database can occur outside of a database transaction (this seems to confuse many developers who are used to the auto-commit mode). Always use clear transaction boundaries, even for read-only operations. Depending on your isolation level and database capabilities this might not be required but there is no downside if you always demarcate transactions explicitly. Certainly, a single database transaction is going to perform better than many small transactions, even for reading data.
A Hibernate application can run in non-managed (i.e. standalone, simple Web- or Swing applications) and managed J2EE environments. In a non-managed environment, Hibernate is usually responsible for its own database connection pool. The application developer has to manually set transaction boundaries, in other words, begin, commit, or rollback database transactions himself. A managed environment usually provides container-managed transactions (CMT), with the transaction assembly defined declaratively in deployment descriptors of EJB session beans, for example. Programmatic transaction demarcation is then no longer necessary.
However, it is often desirable to keep your persistence layer portable between non-managed resource-local environments, and systems that can rely on JTA but use BMT instead of CMT. In both cases you'd use programmatic transaction demaracation. Hibernate offers a wrapper API called Transaction that translates into the native transaction system of your deployment environment. This API is actually optional, but we strongly encourage its use unless you are in a CMT session bean.
Usually, ending a Session involves four distinct phases:
  • flush the session
  • commit the transaction
  • close the session
  • handle exceptions
Flushing the session has been discussed earlier, we'll now have a closer look at transaction demarcation and exception handling in both managed- and non-managed environments.

11.2.1. Non-managed environment

If a Hibernate persistence layer runs in a non-managed environment, database connections are usually handled by simple (i.e. non-DataSource) connection pools from which Hibernate obtains connections as needed. The session/transaction handling idiom looks like this:
// Non-managed environment idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
    tx = sess.beginTransaction();

    // do some work
    ...

    tx.commit();
}
catch (RuntimeException e) {
    if (tx != null) tx.rollback();
    throw e; // or display error message
}
finally {
    sess.close();
}
You don't have to flush() the Session explicitly - the call to commit() automatically triggers the synchronization (depending upon the Section 10.10, “Flushing the Session” FlushMode for the session. A call to close() marks the end of a session. The main implication of close() is that the JDBC connection will be relinquished by the session. This Java code is portable and runs in both non-managed and JTA environments.
A much more flexible solution is Hibernate's built-in "current session" context management, as described earlier:
// Non-managed environment idiom with getCurrentSession()
try {
    factory.getCurrentSession().beginTransaction();

    // do some work
    ...

    factory.getCurrentSession().getTransaction().commit();
}
catch (RuntimeException e) {
    factory.getCurrentSession().getTransaction().rollback();
    throw e; // or display error message
}
You will very likely never see these code snippets in a regular application; fatal (system) exceptions should always be caught at the "top". In other words, the code that executes Hibernate calls (in the persistence layer) and the code that handles RuntimeException (and usually can only clean up and exit) are in different layers. The current context management by Hibernate can significantly simplify this design, as all you need is access to a SessionFactory. Exception handling is discussed later in this chapter.
Note that you should select org.hibernate.transaction.JDBCTransactionFactory (which is the default), and for the second example "thread" as your hibernate.current_session_context_class.