This post is part 4 in a 9-part series on Envers, Spring, and JTA. The next post is The versioning isn’t happening, part 4a: phone a friend, and the previous post is The versioning isn’t happening, part 3: it’s in UserTransaction?
While looking at the javadoc for javax.transaction.Synchronization earlier, I had noticed this:
Using the registerSynchronization method, the application server registers a Synchronization object for the transaction currently associated with the target Transaction object.
Now, the only registerSynchronization() method I see is javax.transaction.Transaction’s registerSynchronization() method, and the one place that registerSynchronization() is called from the envers code is in org.jboss.envers.synchronization.VersionsSyncManager.get(EventSource).
Here’s an interesting thing: when I set a breakpoint on VersionsSyncManager.get(EventSource) and start stepping through the code, the first thing it does (line 47) is do a session.getTransaction(). I expected that underneath, this would be our javax.transaction.UserTransaction (since we’re running in JTA mode), but instead when we call transaction.registerSynchronization() I see that we have a org.hibernate.transaction.JDBCTransaction.
So to step back a moment, what we have here is a Synchronization being registered with a JDBCTransaction, when the actual work will be done by a javax.transaction.UserTransaction. It seems plausible that when the UserTransaction goes to commit, it won’t run these registered synchronizations (since it seems it wouldn’tknow about them).
Why does EventSource.getTransaction() return a JDBCTransaction instead of the UserTransaction?
EventSource extends org.hibernate.Session, and the javadoc for org.hibernate.Session.getTransaction() has this interesting tidbit:
The class of the returned Transaction object is determined by the property hibernate.transaction_factory.
The closest to this that I see in the AnnotationConfig is:
So what does SpringTransactionFactory do?
Hmm: org.springframework.orm.hibernate3.SpringTransactionFactory.createTransaction(JDBCContext, Context) returns a new JDBCTransaction, even if we’re in JTA mode. That is the transaction that is returned (I think) by org.hibernate.Session.getTransaction(). Is that a bug? Or alternatively, is the problem that Spring’s JtaTransactionManager calls commit() on the UserTransaction directly, without taking into account the synchronizations registered on the companion JDBCTransaction object? It seems that one way or another, someone should be taking responsibility for getting those registered callbacks called!
(Next: Time to ask for help!)