Master Distributed Transactions in Spring Boot 3 with Atomikos – Full Code Guide
This article walks through integrating Atomikos with Spring Boot 3 to implement JTA‑based distributed transactions, covering the concepts of Atomikos and JTA, detailed configuration of multiple data sources, domain and repository definitions, service implementation, and comprehensive test cases with expected outcomes.
Spring Boot 3 practical case collection – updated to 139 examples, continuously maintained as a free resource.
1. Introduction
This article demonstrates how to integrate Atomikos with Spring Boot to achieve distributed transactions using JTA.
1.1 What is Atomikos
Atomikos is a third‑party transaction manager that implements the Java Transaction API (JTA). It provides transaction coordination, lifecycle management, failure recovery, and performance optimizations.
JTA : Standard Java interfaces for managing distributed transactions.
Atomikos : A concrete JTA implementation offering interfaces such as javax.transaction.UserTransaction and javax.transaction.TransactionManager.
1.2 What is JTA
JTA (Java Transaction API) enables distributed transaction management across multiple resources (databases, message queues). Core concepts include distributed transactions, the XA protocol, transaction managers, and resource managers.
Distributed transactions – coordinated across multiple resources.
XA protocol – two‑phase commit standard.
Transaction manager – coordinates global transactions.
Resource manager – XA‑compatible drivers (e.g., MySQL).
Spring and Spring Boot provide built‑in support for JTA, allowing developers to declare transaction boundaries without writing low‑level transaction code.
2. Practical Example
2.1 Environment
Spring Boot 2.7.18, Atomikos 4.x, MySQL 5.7.
2.2 Project Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>2.3 Domain Objects
Two entities are placed in separate packages.
// pkg: com.pack.domain.customer
@Entity
@Table(name = "customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "age", nullable = false)
private Integer age;
}
// pkg: com.pack.domain.order
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "code", nullable = false)
private Integer code;
@Column(name = "quantity", nullable = false)
private Integer quantity;
}2.4 Repository Interfaces
// pkg: com.pack.repository.customer
public interface CustomerRepository extends JpaRepository<Customer, Integer> {}
// pkg: com.pack.repository.order
public interface OrderRepository extends JpaRepository<Order, Integer> {}2.5 Service Layer
// pkg: com.pack.service.customer
@Service
public class CustomerService {
@Resource
private CustomerRepository customerRepository;
@Transactional
public void save(Customer customer) {
customerRepository.save(customer);
}
}
// pkg: com.pack.service.order
@Service
public class OrderService {
@Resource
private OrderRepository orderRepository;
@Transactional
public void save(Order order) {
orderRepository.save(order);
throw new RuntimeException("订单发生异常");
}
}2.6 Configuration for Each Data Source
Customer Data Source
@ConfigurationProperties(prefix = "pack.datasource.customer")
public class CustomerDataSourceProperties {
private String jdbcUrl;
private String username;
private String password;
}
@Configuration
@DependsOn("transactionManager")
@EnableJpaRepositories(
basePackages = "com.pack.repository.customer",
entityManagerFactoryRef = "customerEntityManager",
transactionManagerRef = "transactionManager")
@EnableConfigurationProperties(CustomerDataSourceProperties.class)
public class CustomerConfig {
@Resource
private JpaVendorAdapter jpaVendorAdapter;
@Resource
private CustomerDataSourceProperties properties;
@Bean(name = "customerDataSource", initMethod = "init", destroyMethod = "close")
DataSource customerDataSource() throws Exception {
MysqlXADataSource ds = new MysqlXADataSource();
ds.setUrl(properties.getJdbcUrl());
ds.setUser(properties.getUsername());
ds.setPassword(properties.getPassword());
ds.setPinGlobalTxToPhysicalConnection(true);
AtomikosDataSourceBean xaDs = new AtomikosDataSourceBean();
xaDs.setXaDataSource(ds);
xaDs.setUniqueResourceName("xa-customer");
return xaDs;
}
@Bean(name = "customerEntityManager")
@DependsOn("transactionManager")
LocalContainerEntityManagerFactoryBean customerEntityManager() throws Throwable {
HashMap<String, Object> props = new HashMap<>();
props.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
props.put("javax.persistence.transactionType", "JTA");
props.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setJtaDataSource(customerDataSource());
emf.setJpaVendorAdapter(jpaVendorAdapter);
emf.setPackagesToScan("com.pack.domain.customer");
emf.setPersistenceUnitName("customerPersistenceUnit");
emf.setJpaPropertyMap(props);
return emf;
}
}Order Data Source
@ConfigurationProperties(prefix = "pack.datasource.order")
public class OrderDataSourceProperties {
private String jdbcUrl;
private String username;
private String password;
}
@Configuration
@DependsOn("transactionManager")
@EnableJpaRepositories(
basePackages = "com.pack.repository.order",
entityManagerFactoryRef = "orderEntityManager",
transactionManagerRef = "transactionManager")
@EnableConfigurationProperties(OrderDataSourceProperties.class)
public class OrderConfig {
@Resource
private JpaVendorAdapter jpaVendorAdapter;
@Resource
private OrderDataSourceProperties properties;
@Bean(name = "orderDataSource", initMethod = "init", destroyMethod = "close")
DataSource orderDataSource() throws Exception {
MysqlXADataSource ds = new MysqlXADataSource();
ds.setUrl(properties.getJdbcUrl());
ds.setUser(properties.getUsername());
ds.setPassword(properties.getPassword());
ds.setPinGlobalTxToPhysicalConnection(true);
AtomikosDataSourceBean xaDs = new AtomikosDataSourceBean();
xaDs.setXaDataSource(ds);
xaDs.setUniqueResourceName("xa-order");
return xaDs;
}
@Bean(name = "orderEntityManager")
@DependsOn("transactionManager")
LocalContainerEntityManagerFactoryBean orderEntityManager() throws Throwable {
HashMap<String, Object> props = new HashMap<>();
props.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
props.put("javax.persistence.transactionType", "JTA");
props.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setJtaDataSource(orderDataSource());
emf.setJpaVendorAdapter(jpaVendorAdapter);
emf.setPackagesToScan("com.pack.domain.order");
emf.setPersistenceUnitName("orderPersistenceUnit");
emf.setJpaPropertyMap(props);
return emf;
}
}2.7 Main Configuration
@Configuration
@ComponentScan
@EnableTransactionManagement
public class MainConfig {
@Bean
JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(true);
adapter.setGenerateDdl(true);
adapter.setDatabase(Database.MYSQL);
return adapter;
}
@Bean(name = "userTransaction")
UserTransaction userTransaction() throws Throwable {
UserTransactionImp ut = new UserTransactionImp();
ut.setTransactionTimeout(10000);
return ut;
}
@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
TransactionManager atomikosTransactionManager() throws Throwable {
UserTransactionManager utm = new UserTransactionManager();
utm.setForceShutdown(false);
AtomikosJtaPlatform.transactionManager = utm;
return utm;
}
@Bean(name = "transactionManager")
@DependsOn({"userTransaction", "atomikosTransactionManager"})
PlatformTransactionManager transactionManager() throws Throwable {
UserTransaction ut = userTransaction();
AtomikosJtaPlatform.transaction = ut;
TransactionManager tm = atomikosTransactionManager();
return new JtaTransactionManager(ut, tm);
}
}2.8 Test Service
@Service
public class BaseService {
@Resource
private CustomerService customerService;
@Resource
private OrderService orderService;
@Transactional
public void save(Customer customer, Order order) {
customerService.save(customer);
orderService.save(order);
}
@Transactional
public void saveLocalExecption(Customer customer, Order order) {
customerService.save(customer);
orderService.save(order);
throw new RuntimeException("LocalException 发生异常");
}
@Transactional
public void saveCustomerException(Customer customer, Order order) {
customerService.save(customer);
orderService.save(order);
}
}2.9 Test Cases
@Test
public void testSaveNormal() {
Customer customer = new Customer();
customer.setAge(10);
customer.setName("张三");
Order order = new Order();
order.setCode(10001);
order.setQuantity(10);
baseService.save(customer, order);
}
@Test
public void testSaveLocalExecption() {
Customer customer = new Customer();
customer.setAge(10);
customer.setName("张三");
Order order = new Order();
order.setCode(10001);
order.setQuantity(10);
baseService.saveLocalExecption(customer, order);
}Running the normal test commits both transactions and the data appears in the database. The exception tests cause both transactions to roll back, leaving the database empty. This confirms that Atomikos successfully manages distributed transactions in Spring Boot.
For the complete source, refer to the accompanying PDF e‑book (139 cases) and the image diagrams included above.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
