JTA Transaction Type
Are you struggling with performance issues in your Spring, Jakarta EE, or Java EE application?
What if there were a tool that could automatically detect what caused performance issues in your JPA and Hibernate data access layer?
Wouldn’t it be awesome to have such a tool to watch your application and prevent performance issues during development, long before they affect production systems?
Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, Micronaut, or Play Framework.
So, rather than fixing performance issues in your production system on a Saturday night, you are better off using Hypersistence Optimizer to help you prevent those issues so that you can spend your time on the things that you love!
Introduction
In this article, we are going to analyze how the JTA transaction type works.
Since this is the default transaction type when using Jakarta EE or Java EE applications, it’s very important to understand how JTA transactions work, especially since Spring Boot or Spring applications use RESOURCE_LOCAL transactions instead.
JTA Transaction Type
When using the JTA transaction type, you can operate modifications across multiple data sources in an atomic global transaction that either commits all changes or rolls them back.
To understand how JTA transactions work, consider the following diagram that depicts a global JTA transaction modifying both a PostgreSQL database and an Ehcache instance:

JTA Transaction Type
- The application controls the transaction scope via the
UserTransactioninterface. - To participate in a global JTA transaction, a resource must implement the
XAResourceinterface, which provides theprepare,commit, androllbackmethods that will be called by the JTA transaction manager based on the 2PC (Two-Phase Commit) protocol. Our application enlists theEhcacheXAResourceto coordinate the transaction outcome for the Ehcache-related changes and thePGXAConnectionto commit or roll back the changes that were done in the PostgreSQL database. - The application makes changes to the PostgreSQL database and to Ehcache and calls
commiton theUserTransaction. - The transaction manager will call
prepareon eachXAResourceso they can vote on the transaction outcome. This is the first phase of the 2PC protocol that allows the enlisted resources to achieve consensus. - If all resources agree to commit the transaction, the
commitmethod will be called on eachXAResource, and the changes operated by this JTA global transaction will become durable.
Using the JTA Transaction Type with Spring
The JPA specification defines two transaction types you could choose from: JTA and RESOURCE_LOCAL. By default, Jakarta EE and Java EE applications use the JTA transaction type, while Spring Boot uses the RESOURCE_LOCAL transaction type.
To use JTA in a Spring application, you would need a specific bean configuration.
First, you will have to create an instance of the JTA XADataSource interface. For PostgreSQL, we can use the PGXADataSource. Unlike the more common JDBC DataSource, the XADataSource can provide us with the XAResource that can be enlisted in a JTA global transaction.
However, since most data access frameworks operate with the JDBC DataSource, we will have to wrap the PGXADataSource and return an instance of the DataSource interface, as illustrated by the following bean configuration:
@Bean
public DataSource dataSource() {
try {
PGXADataSource dataSource = new PGXADataSource();
dataSource.setUrl(jdbcUrl);
dataSource.setUser(jdbcUser);
dataSource.setPassword(jdbcPassword);
⠀
XARecoveryModule xaRecoveryModule = new XARecoveryModule();
GenericXADataSourceWrapper wrapper =
new GenericXADataSourceWrapper(xaRecoveryModule);
return wrapper.wrapDataSource(dataSource);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
We can then pass the DataSource to the JPA EntityManagerFactory via the Spring LocalContainerEntityManagerFactoryBean configuration:
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setJtaDataSource(dataSource());
emf.setPackagesToScan(packagesToScan());
⠀
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
emf.setJpaVendorAdapter(vendorAdapter);
emf.setJpaProperties(additionalProperties());
return emf;
}
Afterward, we will have to create an instance of the JtaTransactionManager so that the Spring @Transactional methods can use JTA transactions.
However, the Spring JtaTransactionManager is not really a standalone JTA transaction manager, like Narayana or Atomikos. Instead, the JtaTransactionManager is simply an instance of the AbstractPlatformTransactionManager that can work with the Spring TransactionInterceptor Aspect, which is responsible for managing the declarative @Transactional methods.
Therefore, when creating the Spring JtaTransactionManager instance, we need to provide an instance of the standalone JTA transaction manager that implements the Jakarta Transactions specification.
If you are using Narayana, the Spring bean configuration is going to look like this:
@Bean
public JtaTransactionManager transactionManager() {
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
jtaTransactionManager.setTransactionManager(jtaTransactionManager());
jtaTransactionManager.setUserTransaction(jtaUserTransaction());
jtaTransactionManager.setAllowCustomIsolationLevels(true);
return jtaTransactionManager;
}
@Bean
public TransactionManagerImple jtaTransactionManager() {
TransactionManagerImple transactionManager = new TransactionManagerImple();
return transactionManager;
}
@Bean
public UserTransactionImple jtaUserTransaction() {
UserTransactionImple userTransactionManager = new UserTransactionImple();
return userTransactionManager;
}
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
JTA transactions are needed if you have to operate changes on multiple data sources in an atomic fashion.
While the RESOURCE_LOCAL transaction type can manage a single JDBC Connection, JTA allows you to span the transaction boundaries over multiple resources, such as database connections, JMS queues, or caches.






