I am working on a legacy application. We are moving it from JDBC to Spring 3.2 + Hibernate 4.1.12 + JTA 2 with declarative transactions. I see that the Container-Managed Transactions (CMT) are transacting and rolling back as one would expect. We are using Infinispan as the second level cache (2LC). There is one wrinkle...
There is a portion of the code with a different entry point that is run in a different thread and uses programmatic transactions or Bean-Managed Transactions (BMT). In the BMT path, I see that in the underlying service layer, which is using CMT, the transactions are joining with the BMT as one would hope and expect.
The persistence unit, data source, etc. are the same for both entry points. In both cases, the Hibernate autoflush code is aware that there is a transaction and flushes to the database driver. In the CMT entry point, the database driver holds the data until told to commit or rollback. In the BMT path, the data is pushed into the database on flush – the later commit or rollback has no effect or apparent meaning. The transaction manager is the JtaTransactionManager. The JtaTransactionManager is defined in a @Configuration class with @EnableTransactionManagement to enable the CMT rather than the <tx:annotation-driven/>
element.
The singleton JtaTransactionManager bean is wired with the ajuna UserTransaction and TransactionManager via jtaPropertyManager.getJTAEnvironmentBean().getTransactionManager()
and jtaPropertyManager.getJTAEnvironmentBean().getUserTransaction()
. Both the UserTransaction and TransactionManager are prototype @Bean definitions.
I am able to confirm the data is in or not in the database by a query from another query tool to verify the behavior while debugging.
When I am unit testing, the data commits and rolls back as expected for both the BMT and the CMT entry point.
The BMT is managed by a class that has the transaction begin and end in different methods. It also has methods that perform the actual unit of work. The transactions for the BMT are initiated with the PlatformTransactionManager, not the TransactionTemplate. The class is driven by another class that has the logic to manage the logic flow. I know that the transactions are beginning and ending as expected. When reading various other discussion, It seems implied that the transactional control should be within a single method. I would agree that this would be preferred but is it essential?
If a CMT-managed servlet in Spring spawns a new Thread and starts the thread with a plan thread.start(), is it reasonable to expect that a BMT within that new Thread would be able to manage its transactions as described above?
The datasource is retrieved by JNDI. Using XA or non XA does not influence the outcome.
I am unable to post the code.
As a reference, here is the link to the Spring 3.1 docs on transaction in chapter 11.
Added 2013/10/04 - I see that Spring uses the JtaTransactionManagerBeanDefinitionParser
to construct the desired JtaTransactionManager
based on the perceived container. When this is used, the JTA transaction manager will set into itself in the afterPropertiesSet
the UserTransaction, TransactionManager, and TransactionSynchronizationRegistry.
It appears that I do actually still leak data in the CMT but that it is hard to perceive/observe this without a debugger or forcing an error unnaturally since the transactions typically commit.
It appears that my issue is that I have partially bypassed the JCA such that the JCA is using a different TransactionManager.
Partial Answer - Because I have seen this transact properly in a mix of CMT and BMT, I know that it is possible to have the BMT transaction started in one method and committed in another.
The question remains: If a CMT-managed servlet in Spring spawns a new Thread and starts the thread with a plan thread.start(), is it reasonable to expect that a BMT within that new Thread would be able to manage its transactions as described above?