I was a little confused on this point as well. What I gathered is this:
1. An application transaction consists of one or more database transactions
2. The scope of a database transaction should be the same as the scope of a Hibernate transaction
3. A best practice is to limit the scope of a Hibernate transaction to the scope of a request cycle in a web application. Subsequently, a Hibernate transaction (and subsequently the database transaction) should be committed at the end of every request cycle.
4. The application transaction ends when the Hibernate session is closed (or cleared)
5. Ergo, the scope of an application transaction is the same as the scope of a Hibernate session
First, is that correct? I am using user-supplied connections, so I have to explicitly call session.connection( ).commit( ), which means the scope of my Hibernate transactions do not necessarily have to coincide with the scope of my database transactions. Is this the case using managed connections (ala C3P0) as well? Or does a Transaction.commit( ) call commit( ) automatically, thus forcing the scopes to be the same?
Second, if I am correct in assuming that the scope of a Hibernate transaction is the same as that of a database transaction, then I am a little uncertain as to how to handle a failed application transaction. For example, this sequence of events occurs often in our applications:
Request 1: Object is loaded and cached in the Hibernate session. Object is passed to the view, where forms are rendered and pre-populated
Request 2: User submits modifications to the data. These are passed to the business layer and validated. If the data is invalid, messages are generated, passed back up to the View, and rendered (Repeats until data is correct). Otherwise, data is committed and the session is closed.
A commit at the end of Request 1 never hurts, since the only operation performed is a load. A commit at the end of Request 2 is allowable, provided the data is correct. However, if the data is invalid, then the commit may result in a HibernateException (via a SQLException) in the best case, or invalid data (worst case).
To combat this, I have come up with a pattern along the lines of the filter provided in the book, with the following exceptions:
1. I have created a session bean that stores a Hibernate session and Hibernate transaction. This is stored in the user's HTTP session.
2. At the start of each request, I ensure that the session bean contains an open, connected session, and an uncommitted transaction. The session is then copied into a ThreadLocal variable for convenience.
3. During the request, the commitTransaction or rollbackTransaction is called on the session bean to set a flag and "schedule" the commit/rollback of a transaction and closure of the session
4. At the end of the request, if a commit/rollback has been "scheduled", the transaction is committed or rolled back, and the session is closed.
In this pattern, the Hibernate transaction has the same scope as the session, and allows for an all-or-nothing application transaction.
Do you see any pitfalls to this approach? (I tend to come up with anti-patterns...I was unwittingly using session-per-application until very recently).
P.S. The book is awesome...not only did it explain Hibernate, but it really opened my eyes to ORM issues in general. Great read.
- Jesse
|