Session not flushed with Hibernate 4.1.3, JTA and Spring transaction management integ
I have been migrating from hibernate 3.3.x to hibernate 4.1.3-final using Spring 3.1.1-release.
Besides the hibernate specific refactorings (due to API changes) I thought the only changes for the Spring integration were changing packages names:
-Use the LocalSessionFactoryBean from the hibernate4 package
-Use the HibernateTransactionManger (which we only use for testing) from the hibernate4 package.
As it turned out the migration when smooth and everything was working on a local tomcat.
However, once we ran our app on glassfish with the JtaTransactionManager (I tested it on GF3.1.2) we got a "No Session found for current thread" when obtaining sessionFactory.currentSession().
After checking SpringSessionContext, we learned that if TransactionSynchronizationManager does not return a existing session or SessionHolder, a check
is performed for a jtaSessionContext, which was null.
The fact that no SessionHolder is registered made also sense as this done by the HibernateTransactionManager and we are using the JtaTransactionManager.
So then learned that in case of JTA you have to specify manually how the tx manager/user transaction can be found.
This was done automatically for you in the hibernate3 LocalSessionFactoryBean, but no longer in the hibernate4 LocalSessionFactoryBean.
So to solve this we configured:
hibernate.transaction.jta.platform and set it to SunOneJtaPlatform.
This resolved the "No Session found for current thread" as it initialized the jtaSessionContext with the txmanager provided by the configured JtaPlatform.
However, now it turns out that the hibernate session is not flushed before the transaction is commited and hence no modifications are written to database.
In the supplied sample we have a basic hibernate setup with H2. Next we have a JtaTransactionManager and a transactional facade.
Next we have a test entity having a single property. The facade has two methods, one to store the entity and one to retrieve the entity.
They both marked as @Transactional and if called will run in there own transaction.
The trigger is a plain JEE servlet which retrieves the facade from application context.
First the store method is called (tx1) then the retrieve method is called (tx2).
As you will see with the Spring hibernate4 integration there was nothing saved.
With the hibernate3 integration everything works as expected and the record is saved
(it can be retrieved by the subsequent retrieval transaction)
What is also bizarre is that in hibernate3 modus everything goes via the Spring TransactionSynchronizationManager (even in JTA mode).
Also the current session is bound via a thread local referenced via the synchronization.
This is bound using a SpringSessionSynchronization which will call flush before transaction completion.
All of this is gone with the hibernate4 integration from the moment a JTA environment is detected.
As of then everything goes via the JTA transaction manager, as there where no Spring Transacion management involved.
This could be normal to a certain extend, but it feels odd compared to the way is was done with hibernate3
I supplied two samples:
-hibernate3.zip : this is the working one, deploy it on GF and goto "http://localhost:8080/hibernate3/Persist"
You will see that it stores a record and is able to retrieve it again
-hibernate4.zip : the exact same sample as above, but now with hibernate4 and using LocalSessionFactoryBean from the hibernate4 package and the hibernate.transaction.jta.platform set.
You will see that it stores a record and is NOT able to retrieve it.
Both samples have a POM so it should be trivial to build them.
I´ve set hibernate.transaction.factory_class to org.hibernate.engine.transaction.internal.jta.CMTT ransactionFactory and hibernate.transaction.jta.platform to org.hibernate.service.jta.platform.internal.JBossA ppServerJtaPlatform. My transaction manager bean is a org.springframework.transaction.jta.JtaTransaction Manager configured bean.
Then, logging (TRACE mode) shows me the following info:
HOWEVER IF I use org.springframework.orm.jpa.JpaTransactionManager, instead of JtaTransactionManager, adjusting the configurations above, logging shows me:
SO, is something missing? Some additional configuration? Using JpaTransactionaManager everything works as expected, but when using JtaTransactionManager there is no auto flush during the before completion phase.
Could you please help us?
I´m looking forward to hearing good news from the Spring guys.
alysson.rodrigues: The JpaTransactionManager will probably act the same way as the HibernateTransactionManager.
If I replace the JtaTransactionManager with the HibernateTransactionManager everything also works as expected, and I also see the flush.
However, the Jpa/HibernateTransactionManager are single resource transaction managers and cannot be used if multiple resources need to participate in the transaction.
So switching to the HibernateTransactionManager is not an option.
What happens in case of direct hibernate access is that the SpringSessionContext will detect a SessionHolder object registered by the HibernateTransactionManager.
In that case it will register a SpringSessionSynchronization (SpringSessionContext L75): TransactionSynchronizationManager.registerSynchron ization(new SpringSessionSynchronization(sessionHolder, this.sessionFactory)); which will eventually flush the session before the transaction is commited.
I suspect that in case of the JpaTransactionManager something similar is happening.
However, if JTA mode is detected no such synchronization are registered and no one is flushing the session.
The only workaround this far is creating an aspect that listens on the @Transactional annotation and flushes the session.
You can change the order of the <tx:annotation-driven/> (or @EnableTransactionManagement annotation) using the "order" attribute.
You would then give the <tx:annotation-driven/> a lower order then your aspect listing for the @Transactional annotation, this way your aspect is called after the transaction is started and before it is committed (allowing it to flush the session).
koen.serneels: what about the SpringSessionContext.currentSession L89 and the SpringFlushSynchronization class itself?
<code>
else if (this.jtaSessionContext != null) {
Session session = this.jtaSessionContext.currentSession();
if (TransactionSynchronizationManager.isSynchronizati onActive()) {
TransactionSynchronizationManager.registerSynchron ization(new SpringFlushSynchronization(session));
}
return session;
}
</code>
Are not these lines and class supposed to do the same work as you mentioned?
Logging shows me that Spring has detected the transaction manager, user transaction and a transaction synchronization registry. I was wondering if I have to set something to get it working with SpringFlushSynchronization.
17:38:58,948 DEBUG [org.springframework.jndi.JndiTemplate] (MSC service thread 1-7) Looking up JNDI object with name [java:/TransactionManager]
17:38:58,949 DEBUG [org.springframework.transaction.jta.JtaTransaction Manager] (MSC service thread 1-7) JTA TransactionManager found at fallback JNDI location [java:/TransactionManager]
17:38:58,950 INFO [org.springframework.transaction.jta.JtaTransaction Manager] (MSC service thread 1-7) Using JTA UserTransaction: org.jboss.tm.usertx.client.ServerVMClientUserTrans action@192a63a
17:38:58,950 INFO [org.springframework.transaction.jta.JtaTransaction Manager] (MSC service thread 1-7) Using JTA TransactionManager: com.arjuna.ats.jbossatx.jta.TransactionManagerDele gate@4383e3
17:38:58,951 DEBUG [org.springframework.jndi.JndiTemplate] (MSC service thread 1-7) Looking up JNDI object with name [java:comp/TransactionSynchronizationRegistry]
17:38:58,952 DEBUG [org.springframework.transaction.jta.JtaTransaction Manager] (MSC service thread 1-7) JTA TransactionSynchronizationRegistry found at default JNDI location [java:comp/TransactionSynchronizationRegistry]
17:38:58,953 INFO [org.springframework.transaction.jta.JtaTransaction Manager] (MSC service thread 1-7) Using JTA TransactionSynchronizationRegistry: com.arjuna.ats.internal.jta.transaction.arjunacore .TransactionSynchronizationReg
istryImple@87e18f
alysson.rodrigues: Yes, the code you mention is indeed the code which gets executed in case of a JTA context.
You would indeed also think that the "SpringFlushSynchronization" (as the name implies) will flush the session.
But that is not happening. If you look for references you will notice that TransactionStatus.flush() is only called from classes in the JDO package. So the synchronization registered here is not invoked. Why, I don't know...
So the main question remains, is this a bug or is there (like you said) some missing configuration...
Apparently it could be solved by setting the property hibernate.transaction.factory_class which resolves default to the JDBC transaction factory depending on the hibernate transaction API. See: https://jira.springsource.org/browse/SPR-9404