This post is part 1 in a 9-part series on Envers, Spring, and JTA. The next post is The versioning isn’t happening, part 2: beforeCompletion.
We have a .war project that uses envers to update a revision table when a record is created, updated, or deleted in a certain table.
0. The Problem
When the project is deployed configured to connect to a Hibernate transaction manager:
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
then when I make the web service call that results in a record being written to the database, the envers versioning records are also created; but when the project is deployed configured to connect to the JTA transaction manager:
the envers versioning records are not created.
Following are some steps I took, in a simplified ordering. (I actually jumped back and forth a lot more between debugging in Eclipse and setting up to examine log output). I’ve left out a lot of the running around in circles and some of the dead ends. :)
1. The Sanity Check
At the envers user forum, I inquired whether envers is known to work with a JTA; and a kind soul replied that they’re using envers with JTA and it’s working fine for them.
2. Using Log messages
I added this to jboss-log4j.xml:
<logger name="org.hibernate.jdbc"> <level value="TRACE" /> <appender-ref ref="HibernateLog" /> </logger> <logger name="org.hibernate.transaction"> <level value="TRACE" /> <appender-ref ref="HibernateLog" /> </logger>
When I compared the JBoss server.log files after testing my web-service-call-that-writes-a-record-to-the-database using a JTA transaction manager versus using the Hibernate transaction manager, I noticed that the JTA version doesn’t seem to make use of the org.hibernate.transaction.JDBCTransaction class.
3. The Helpfulness of Java Search
I wondered, what code is responsible for choosing where the JDBCTransaction class is used? I wanted to do a search that included the source attachments in my project’s Referenced Libraries, but Eclipse’s File Search (the search I use for everything) didn’t seem to do that. So I tried the Java Search tab, searching for
- Search string: org.hibernate.transaction.JDBCTransaction
- Search For: Type
- Limit To: References
- Search In: Sources, Required projects, JRE libraries, Application libraries
- Scope: Workspace
and lo and behold, it searched in the referenced libraries and found two places that reference JDBCTransaction:
- org.hibernate.transaction.JDBCTransactionFactory in hibernate-3.2.6.ga.jar
- org.springframework.orm.hibernate3.SpringTransactionFactory in spring-orm-2.5.5.jar
I put a class load breakpoint on each of these classes, and deployed the Hibernate version of the project, my guess being that the problem may be occurring before the first message is sent, when the project is being deployed and setting up with the transaction manager.
5. Who loaded me?
I brought JBoss-with-remote-debugging up and hit the class load breakpoint for the SpringTransactionFactory. Not recognizing where the call was coming from (it was being invoked via reflection), I went to the Outline view, selected all the methods of SpringTransactionFactory including its constructor, set method entry breakpoints, and resumed. This time it hit org.hibernate.transaction.TransactionFactoryFactory.buildTransactionFactory(), called from org.hibernate.cfg.SettingsFactory.buildSettings(), which is called from org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory().
Hmm, I bet the hibernate3.LocalSessionFactoryBean is not used when we’re in JTA mode. I put class load breakpoints on TransactionFactoryFactory, SettingsFactory, and LocalSessionFactoryBean to see if any of these are used when we’re in jta mode.
7. Trying it in JTA mode
The first breakpoint we hit in jta mode is during the creation of the sessionFactory bean. And… huh, at this point we are in org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(). The newSessionFactory() call is at the bottom of this method… let’s see if we make it down there in JTA mode.
Hmm, this.jtaTransactionManager is null on line 524 in LocalSessionFactoryBean… (I don’t know if that’s good or bad…)
org.springframework.orm.hibernate3.AbstractSessionFactoryBean.isExposeTransactionAwareSessionFactory() returns true on line 550 — I wonder if that was the case in Hibernate mode? — this results in the hibernate.current_session_context_class property being set in the AnnotationConfiguration being built.
8. Trying it in Hibernate mode
I deployed the Hibernate version of my .war project, and…
- org.springframework.orm.hibernate3.AbstractSessionFactoryBean.isExposeTransactionAwareSessionFactory() does return true on line 550 just like in jta mode.
- When I examine the AnnotationConfiguration named config‘s properties field (In org.springframework.orm.hibernate3.LocalSessionFactoryBean’s buildSessionFactory() where the AnnotationConfiguration is done being built) and compare its contents to when I run in jta mode… they’re identical except for one property:
So, I still haven’t discovered the source of the difference between the Hibernate and JTA modes.
Perhaps next I should capture the values of the rest of AnnotationConfiguration’s 47 fields, and compare them all between jta mode and Hibernate mode. (I somehow didn’t expect the properties to be the same…!)