本文源码:GitHub·点这里 || GitEE·点这里
一、JTA组件简介
1、JTA基本概念
JTA即Java-Transaction-API,JTA允许应用程序执行分布式事务处理,即在两个或多个网络计算机资源上访问并且更新数据。JDBC驱动程序对JTA的支持极大地增强了数据访问能力。
XA协议是数据库层面的一套分布式事务管理的规范,JTA是XA协议在Java中的实现,多个数据库或是消息厂商实现JTA接口,开发人员只需要调用SpringJTA接口即可实现JTA事务管理功能。
JTA事务比JDBC事务更强大。一个JTA事务可以有多个参与者,而一个JDBC事务则被限定在一个单一的数据库连接。下列任一个Java平台的组件都可以参与到一个JTA事务中
2、分布式事务
分布式事务(DistributedTransaction)包括事务管理器(TransactionManager)和一个或多个支持 XA 协议的资源管理器 ( Resource Manager )。
资源管理器是任意类型的持久化数据存储容器,例如在开发中常用的关系型数据库:MySQL,Oracle等,消息中间件RocketMQ、RabbitMQ等。
事务管理器提供事务声明,事务资源管理,同步,事务上下文传播等功能,并且负责着所有事务参与单元者的相互通讯的责任。JTA规范定义了事务管理器与其他事务参与者交互的接口,其他的事务参与者与事务管理器进行交互。
二、SpringBoot整合JTA
项目整体结构图

1、核心依赖
1<!--SpringBoot核心依赖--> 2<dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-starter-web</artifactId> 5</dependency> 6<!--JTA组件核心依赖--> 7<dependency> 8 <groupId>org.springframework.boot</groupId> 9 <artifactId>spring-boot-starter-jta-atomikos</artifactId> 10</dependency>
2、环境配置
这里jtaManager的配置,在日志输出中非常关键。
1spring: 2 jta: 3 transaction-manager-id: jtaManager 4 # 数据源配置 5 datasource: 6 type: com.alibaba.druid.pool.DruidDataSource 7 data01: 8 driverClassName: com.mysql.jdbc.Driver 9 dbUrl: jdbc:mysql://localhost:3306/data-one 10 username: root 11 password: 000000 12 data02: 13 driverClassName: com.mysql.jdbc.Driver 14 dbUrl: jdbc:mysql://localhost:3306/data-two 15 username: root 16 password: 000000
3、核心容器
这里两个数据库连接的配置手法都是一样的,可以在源码中自行下载阅读。基本思路都是把数据源交给JTA组件来统一管理,方便事务的通信。
数据源参数
1@Component 2@ConfigurationProperties(prefix = "spring.datasource.data01") 3public class DruidOneParam { 4 private String dbUrl; 5 private String username; 6 private String password; 7 private String driverClassName; 8}
JTA组件配置
1package com.jta.source.conifg; 2 3@Configuration 4@MapperScan(basePackages = {"com.jta.source.mapper.one"},sqlSessionTemplateRef = "data01SqlSessionTemplate") 5public class DruidOneConfig { 6 7 private static final Logger LOGGER = LoggerFactory.getLogger(DruidOneConfig.class) ; 8 9 @Resource 10 private DruidOneParam druidOneParam ; 11 12 @Primary 13 @Bean("dataSourceOne") 14 public DataSource dataSourceOne () { 15 16 // 设置数据库连接 17 MysqlXADataSource mysqlXADataSource = new MysqlXADataSource(); 18 mysqlXADataSource.setUrl(druidOneParam.getDbUrl()); 19 mysqlXADataSource.setUser(druidOneParam.getUsername()); 20 mysqlXADataSource.setPassword(druidOneParam.getPassword()); 21 mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true); 22 23 // 事务管理器 24 AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean(); 25 atomikosDataSourceBean.setXaDataSource(mysqlXADataSource); 26 atomikosDataSourceBean.setUniqueResourceName("dataSourceOne"); 27 return atomikosDataSourceBean; 28 } 29 30 @Primary 31 @Bean(name = "sqlSessionFactoryOne") 32 public SqlSessionFactory sqlSessionFactoryOne( 33 @Qualifier("dataSourceOne") DataSource dataSourceOne) throws Exception{ 34 // 配置Session工厂 35 SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); 36 sessionFactory.setDataSource(dataSourceOne); 37 ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); 38 sessionFactory.setMapperLocations(resolver.getResources("classpath*:/dataOneMapper/*.xml")); 39 return sessionFactory.getObject(); 40 } 41 42 @Primary 43 @Bean(name = "data01SqlSessionTemplate") 44 public SqlSessionTemplate sqlSessionTemplate( 45 @Qualifier("sqlSessionFactoryOne") SqlSessionFactory sqlSessionFactory) { 46 // 配置Session模板 47 return new SqlSessionTemplate(sqlSessionFactory); 48 } 49}
4、测试对比
这里通过两个方法测试结果做对比,在两个数据源之间进行数据操作时,只需要在接口方法加上@Transactional注解即可,这样保证数据在两个数据源间也可以保证一致性。
1@Service 2public class TransferServiceImpl implements TransferService { 3 4 @Resource 5 private UserAccount01Mapper userAccount01Mapper ; 6 7 @Resource 8 private UserAccount02Mapper userAccount02Mapper ; 9 10 @Override 11 public void transfer01() { 12 userAccount01Mapper.transfer("jack",100); 13 System.out.println("i="+1/0); 14 userAccount02Mapper.transfer("tom",100); 15 } 16 17 @Transactional 18 @Override 19 public void transfer02() { 20 userAccount01Mapper.transfer("jack",200); 21 System.out.println("i="+1/0); 22 userAccount02Mapper.transfer("tom",200); 23 } 24}
三、JTA组件小结
在上面JTA实现多数据源的事务管理,使用方式还是相对简单,通过两阶段的提交,可以同时管理多个数据源的事务。但是暴露出的问题也非常明显,就是比较严重的性能问题,由于同时操作多个数据源,如果其中一个数据源获取数据的时间过长,会导致整个请求都非常的长,事务时间太长,锁数据的时间就会太长,自然就会导致低性能和低吞吐量。
因此在实际开发过程中,对性能要求比较高的系统很少使用JTA组件做事务管理。作为一个轻量级的分布式事务解决方案,在小的系统中还是值得推荐尝试的。
最后作为Java下的API,原理和用法还是值得学习一下,开阔眼界和思路。
四、源代码地址
1GitHub·地址 2https://github.com/cicadasmile/middle-ware-parent 3GitEE·地址 4https://gitee.com/cicadasmile/middle-ware-parent
