I need your precious help to understand how JTA transactions do actually work in Spring Boot; no matter how much I try, there's always something that sounds weird to me.
As you may know, with Spring Boot you may use one of the supported out of the box JTA transaction managers, namely Atomikos, Bitronix and Narayana, simply adding a spring-boot-starter-jta-xxx dependency in your POM file. This way, SpringBoot automagically wires all required beans
to run a JTA transaction manager. Because I need more than a single Datasource, I cannot simply use "default" JPATransactionManager . Under the hood, I'm going to use Hibernate.
First, I've defined my Hibernate configuration this way:
where CustomJTAPlatform is the following helper class, aimed to provide to Hibernate a reference to actual Transaction manager and UserTransaction istances (actual instances will be provided by spring jta starter package via autoconfiguration)
The following pieces of code define datasources and EntityManager configurations:
Last, in a service class, I delete and persist a simple Entity (Person) on two different databases, in a transactional way:
The above code actually works; data are committed and if I throw an exception after the second savePerson method invocation, data are rollbacked. The problem is that I can't understand what really happens. For example, if I use Atomikos JTA provider, I can read in the logs:
And this makes me think that a transaction is used. Now, if I change my Datasource definition using an AtomicSpecific wrapper for XA datasources:
I get a long log list where XA resources are enlisted.
This fact makes me think that here I have a XA transaction, while, in the first case, despite the fact entities were committed or rolledback, a local transaction took place, not an XA transaction.
This hypotesis seems to be confirmed if I change Atomikos with Bitronix : the log is more explicit:
It's obvious that, even entities are persisted, only a local transaction is performed.
Can anyone help me to understand what I'm missing ?
My mistake was to believe that a JTA Transaction manager will always use a XATransaction when using XADatasource. But that assumption is false. An XADatasource is only capable of support XA Transactions, but also can be used within a local transaction as well.
Moreover, using a XADatasource doesn't imply that the Transaction Manager is aware of it. One has to wrap the specific XADataSource for a given Database using an helper class that is able to enlist a connection (or better, a resource) within an active transaction, otherwise datasources will be treated as non-XA resources.
That explains why I need to use an AtomikosDataSourceBean to wrap an underlying MySQL datasource.
Exactly the same results are achieved with Bitronix implementation of a XADatasource wrapper; if I wrap my datasource using PoolingDataSourceBean (provided by Bitronix library) I get the warning "executing transaction with 0 enlisted resource " go away.
Good heavens! What have you done! Here, try to fix it with this tiny ad: