Blog

Configuring Hibernate and Spring for JTA

18 Jul, 2008
Xebia Background Header Wave

Spring is a great framework for dependency injection and it comes with a lot of support classes and utilities for all kind of things. Hibernate is a persistence service with a lot of useful features, that is relatively easy to use. Configuring both frameworks is not always easy. Configuring them together is sometimes hard and it is easy to make mistakes.
This blog addresses a problem in a configuration that is fairly common: use Spring for transaction management on top of a JTA provider and use Hibernate for persistence. Transaction demarcation is easy and declarative with Spring. The problem is that Hibernate sometimes needs to detect the current transaction and this needs to be configured. This leads to hard to detect bugs in applications that rely on auto flushing.

Auto Flushing
Auto flushing is a Hibernate feature which will synchronize in-memory state with database state when a query is executed. Normally, Hibernate will only synchronize in-memory and database state when the session is flushed. With Spring this can be configured to occur when the transaction is committed. This might lead to unexpected behaviour. Look at the code below

  public void transactionInterceptedMethod() {
    Person p = new Person("Maarten");
    personDao.save(p);
    List persons = personDao.findAll();
  }

Will the new person be in the list of persons? I think everybody would suspect so. In fact, this relies heavily on how Spring and Hibernate are configured.

  1. The person object will not immediately be inserted into the database when offered to Hibernate. It will be kept in memory, until the session is flushed. (There are exceptions, but this is the general rule).
  2. The findAll method will look in the database in the same transaction as the one that is used to insert the person object. Still it will only find the new person if it has been inserted into the database in the same transaction before the query is made.
  3. Spring will commit the transaction and instruct Hibernate to flush the current session only at the end of the method.

All these circumstances influence the result of the findAll method. It seems that we can only have the new person object in the list if we flush the session between the save and findAll methods.
This is where auto flushing comes in. When the flush mode of the session is set to “AUTO”, Hibernate will inspect the in-memory state to see if there are any changes that might influence the result of any query that it is about to execute. If Hibernate determines this is so, it will flush the session, synchronizing the in-memory state and the database state. This will not alter the transactional status, but inly execute some SQL statements within the current transactional context.
Auto-flushing is quite intelligent in what to flush when, although some situations might be hard to detect. To improve performance, Hibernate will not simply always flush everything, but look at the tables in the database that the query touches and the data in-memory that should be stored in those tables. If there is no overlap, no data will be persisted. If an overlap is found, the whole session will be flushed, to ensure consistency.
Spring, JTA and transaction detection
Now this all seems to work out fine, but what does it have to do with Spring, JTA or transaction detection? Well, Hibernate will only perform auto flushing if it detects that there is a transaction running. It is not completely clear to me why it won’t do the flushing, but this is how it works. It has a number of strategies for detecting that there is a transaction. The default strategy is to look at its own transaction mechanism.
When using Spring, to manage transactions, using Hibernates mechanism for transactions (which is a thin layer on top of JDBC) is an option. This mechanism does not support distributed transactions. To support distributed transactions, Spring says you can just drop in their JtaTransactionManager to replace the HibernateTransactionManager. This is not completely true though: It will work seamlessly for transaction demarcation, but not for transaction detection through hibernate.
Transaction demarcation will work fine: Spring will start a new Hibernate session whenever a transaction is started. Hibernate will work with that session, oblivious of the transaction that has been opened. This is not a problem for demarcation: Spring will flush and close the session when the transaction is closed.
For Hibernates transaction detection it does not work: Hibernate is oblivious to the Spring started transaction and it will not do auto flushing. There are not many features in Hibernate that depend upon transaction detection, so this misbehavior of the system might go undetected for quite a while. The problem with auto flushing is that it is not always as simple as two consecutive lines doing a save and findAll. The logic that saves an entity and the logic that depends upon the entity being found by a query within the same transaction might be in separate classes, only connected by a typical flow.
Solution
So how do we tell Hibernate what strategy to use for transaction detection? There are two possibilities: 1) configure Hibernate directly or 2) configure Hibernate through Spring.
To configure Hibernate directly, use the hibernate.transaction.manager_lookup_class and hibernate.transaction.factory_class. These properties would be used by Hibernate to start transactions, but we’re not starting transactions through Hibernate, just detecting them.
To configure Hibernate through Spring, inject a JtaTransactionManager into the LocalSessionFactoryBean. Depending on the specific version of Spring you are using, additional settings must be changed.
Conclusion
Developing with powerful frameworks like Spring and Hibernate makes many tasks easy. It is of great importance that the frameworks are correctly configured, especially if they need to work together. I hope blog will help you detect a particular small bug in your configuration, that could lead to severe problems.

Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts