1
votes

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?

1

1 Answers

1
votes

From JTA 1.1 Specification (http://download.oracle.com/otn-pub/jcp/jta-1.1-spec-oth-JSpec/jta-1_1-spec.pdf) section 3.1, it is clear that the transaction is bound to the thread. This is managed by the TransactionManager. One should be able to expect the thread to be able to perform actions within a transactional context if the thread is the one that created the transaction.

Note that the support of nested transactions is optional as cited in the same portion of the JTA specification.

The actual issue I was encountering was that the managed datasource was using a different instance of the transaction manager than we had as a bean in the application. Changing the application code to do a JNDI lookup of the container-provided TransactionManager allowed the managed datasource to participate in the same transaction as the application.