分布式事务JTA实现Atomikos与Spring集成实践

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
云原生数据库 PolarDB MySQL 版,通用型 2核8GB 50GB
简介:

  Atomikos官网无法访问,不过Maven中央库中具atomikos包。Atomikos集成Spring,Hibernate,Mybatis网上文章比较多,本文是通过JavaSE的方式借用Spring配置来测试Atomikos对JTA的实现。


 下面做一件事,就是两(+)个数据库,在一个事务里对其分别对数据库操作验证操作的原子性,即要么两个数据库的操作都成功,要么都失败。


 1.准备工作

  1.1 Maven pom.xml中添加依赖包

  atomikos:(目前最新版)

  

1
2
3
4
5
< dependency >
             < groupId >com.atomikos</ groupId >
             < artifactId >transactions-jdbc</ artifactId >
             < version >3.9.3</ version >
         </ dependency >


 jar依赖图:

 wKiom1Vu-WuiaLnzAACxcXC9yhs625.jpg

 

 Postgresql数据库驱动:

1
2
3
4
5
< dependency >
             < groupId >org.postgresql</ groupId >
             < artifactId >postgresql</ artifactId >
             < version >9.2-1004-jdbc4</ version >
         </ dependency >

 注:文中使用的是Postgresql V9.2

 

 javax.transaction.transaction-api.1.1:

 

1
2
3
4
5
6
7
8
<!-- Include in javaee-api -->
         <!-- 
         <dependency>
             <groupId>javax.transaction</groupId>
             <artifactId>transaction-api</artifactId>
             <version>1.1</version>
         </dependency>
          -->

 

 

1
2
3
4
5
6
7
<!-- JavaEE -->
         <!-- javaee-api包含了JavaEE规范中的api,如servlet-api,persistence-api, transaction-api等 -->
         < dependency >
             < groupId >javax</ groupId >
             < artifactId >javaee-api</ artifactId >
             < version >7.0</ version >
         </ dependency >

 注:根据需要选择其一即可。


 Spring,Junit依赖这里省略。


 1.2 创建数据库以及表

   数据库分别是:javaee,tomdb

   wKioL1Vu-8eAXWY-AADUZDXsd7M684.jpg


 2.在项目中添加配置文件

  spring-jta.xml 和transaction.properties(模版见文中附件)文件,spring-jta.xml在src/main/resources/integration下,transaction.properties在src/main/resources/下。


 2.1在spring-jta.xml中配置两个XADataSource:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!-- 使用分布式事务时设置Postgresql的max_prepared_transactions为大于0的值,该值默认是0 -->
     <!-- 数据库A -->
     < bean  id = "a"  class = "com.atomikos.jdbc.AtomikosDataSourceBean"
         init-method = "init"  destroy-method = "close" >
         < property  name = "uniqueResourceName"  value = "pg/a"  />
         < property  name = "xaDataSourceClassName"  value = "org.postgresql.xa.PGXADataSource"  />
         < property  name = "xaProperties" >
             < props >
                 < prop  key = "user" >postgres</ prop >
                 < prop  key = "password" >postgres</ prop >
                 < prop  key = "serverName" >localhost</ prop >
                 < prop  key = "portNumber" >5432</ prop >
                 < prop  key = "databaseName" >tomdb</ prop >
             </ props >
         </ property >
         < property  name = "poolSize"  value = "10"  />
         < property  name = "reapTimeout"  value = "20000"  />
     </ bean >
     < bean  id = "b"  class = "com.atomikos.jdbc.AtomikosDataSourceBean"
         init-method = "init"  destroy-method = "close" >
         < property  name = "uniqueResourceName"  value = "pg/b"  />
         < property  name = "xaDataSourceClassName"  value = "org.postgresql.xa.PGXADataSource"  />
         < property  name = "xaProperties" >
             < props >
                 < prop  key = "user" >postgres</ prop >
                 < prop  key = "password" >postgres</ prop >
                 < prop  key = "serverName" >localhost</ prop >
                 < prop  key = "portNumber" >5432</ prop >
                 < prop  key = "databaseName" >javaee</ prop >
             </ props >
         </ property >
         < property  name = "poolSize"  value = "10"  />
         < property  name = "reapTimeout"  value = "20000"  />
     </ bean >


 说明:

 Postgresql的max_prepared_transactions参数值默认是0,要开启XA需要设置该值至少和max_connections参数值一样大,该参数在PostgreSQL\9.3\data\postgresql.conf文件中。

 PGXADataSource的父类BaseDataSource没有url属性,可需要分别设置serverName,portNumber,databaseName等属性。不同的数据库驱动有不同的实现方法。


 2.2 配置事务管理对象和UserTransaction接口实现

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- atomikos事务管理 -->
     < bean  id = "atomikosUserTransactionManager"  class = "com.atomikos.icatch.jta.UserTransactionManager"   init-method = "init"  destroy-method = "close" >
         < description >UserTransactionManager</ description >
         < property  name = "forceShutdown"  value = "true"  />
     </ bean >
 
     < bean  id = "atomikosUserTransaction"  class = "com.atomikos.icatch.jta.UserTransactionImp" >
         < property  name = "transactionTimeout"  value = "300"  />
     </ bean >
 
     < bean  id = "transactionManager"
         class = "org.springframework.transaction.jta.JtaTransactionManager" >
         < property  name = "transactionManager"  ref = "atomikosUserTransactionManager" ></ property >
     </ bean >

 

 上面三个Bean可以独立使用来进行事务控制,具体看下面3。


3. 编写测试

 3.1 使用atomikosUserTransactionManager对象测试(TestAtomikos1.java)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package  secondriver.springsubway.example.jta;
 
import  java.sql.Connection;
import  java.sql.SQLException;
 
import  javax.transaction.HeuristicMixedException;
import  javax.transaction.HeuristicRollbackException;
import  javax.transaction.NotSupportedException;
import  javax.transaction.RollbackException;
import  javax.transaction.SystemException;
 
import  org.junit.BeforeClass;
import  org.junit.Test;
import  org.springframework.context.ApplicationContext;
import  org.springframework.context.support.ClassPathXmlApplicationContext;
 
import  com.atomikos.icatch.jta.UserTransactionManager;
import  com.atomikos.jdbc.AtomikosDataSourceBean;
 
public  class  TestAtomikos1 {
 
     public  static  ApplicationContext ctx;
 
     @BeforeClass
     public  static  void  beforeClass() {
         ctx =  new  ClassPathXmlApplicationContext(
                 "classpath:integration/spring-jta.xml" );
     }
 
     public  static  void  afterClass() {
         ctx =  null ;
     }
 
     @Test
     public  void  test1() {
         exe( "abc" "abc" );
     }
 
     @Test
     public  void  test2() {
         exe( "123=" "123" );
     }
 
     public  void  exe(String av, String bv) {
 
         AtomikosDataSourceBean adsA = (AtomikosDataSourceBean) ctx.getBean( "a" );
         AtomikosDataSourceBean adsB = (AtomikosDataSourceBean) ctx.getBean( "b" );
         Connection connA;
         Connection connB;
         UserTransactionManager utm = (UserTransactionManager) ctx
                 .getBean( "atomikosUserTransactionManager" );
         try  {
             utm.begin();
             connA = adsA.getConnection();
             connB = adsB.getConnection();
             connA.prepareStatement(
                     "insert into jta_temp (value) values('"  + av +  "')" )
                     .execute();
             connB.prepareStatement(
                     "insert into jta_temp (value) values('"  + bv +  "')" )
                     .execute();
             utm.commit();
         catch  (SQLException | NotSupportedException | SystemException
                 | SecurityException | IllegalStateException | RollbackException
                 | HeuristicMixedException | HeuristicRollbackException e) {
             e.printStackTrace();
             try  {
                 utm.rollback();
             catch  (IllegalStateException | SecurityException
                     | SystemException e1) {
                 e1.printStackTrace();
             }
         }
     }
}


3.2使用Spring的JtaUserTransactionManager对象测试(TestAtomikos2.java 修改TestAtomikos1.java中的exe方法即可)

 

1
2
3
4
5
6
7
8
9
@Test
     public  void  test1() {
         exe( "abc" "abc" );
     }
 
     @Test
     public  void  test2() {
         exe( "123=" "123" );
     }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public  void  exe(String av, String bv) {
         TransactionFactory txm = (TransactionFactory) ctx
                 .getBean( "transactionManager" );
 
         JdbcTemplate a = (JdbcTemplate) ctx.getBean( "jdbcTemplateA" );
         JdbcTemplate b = (JdbcTemplate) ctx.getBean( "jdbcTemplateB" );
         Transaction tx = null ;
         try  {
          tx = txm.createTransaction( "tx-name-define" 10000 );
             a.update( "insert into jta_temp (value) values('"  + av +  "')" );
             b.update( "insert into jta_temp (value) values('"  + bv +  "')" );
             tx.commit();
         catch  (NotSupportedException | SystemException | SecurityException
                 | RollbackException | HeuristicMixedException
                 | HeuristicRollbackException e) {
             e.printStackTrace();
             if (tx!= null ){
                 try  {
                     tx.rollback();
                 catch  (IllegalStateException | SystemException e1) {
                     e1.printStackTrace();
                 }
             }
         }
     }

 

3.3使用atomikosUserTransaction Bean对象进行测试(TestAtomikos3.java 修改TestAtomikos1.java中的exe方法即可)

1
2
3
4
5
6
7
8
9
@Test
     public  void  test1() {
         exe( "abc" "abc" );
     }
 
     @Test
     public  void  test2() {
         exe( "123" "123=" );
     }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public  void  exe(String av, String bv) {
 
         AtomikosDataSourceBean adsA = (AtomikosDataSourceBean) ctx.getBean( "a" );
         AtomikosDataSourceBean adsB = (AtomikosDataSourceBean) ctx.getBean( "b" );
         Connection connA;
         Connection connB;
         UserTransaction utx = (UserTransaction) ctx
                 .getBean( "atomikosUserTransaction" );
         try  {
             utx.begin();
             connA = adsA.getConnection();
             connB = adsB.getConnection();
             connA.prepareStatement(
                     "insert into jta_temp (value) values('"  + av +  "')" )
                     .execute();
             connB.prepareStatement(
                     "insert into jta_temp (value) values('"  + bv +  "')" )
                     .execute();
             utx.commit();
         catch  (SQLException | NotSupportedException | SystemException
                 | SecurityException | IllegalStateException | RollbackException
                 | HeuristicMixedException | HeuristicRollbackException e) {
             e.printStackTrace();
             try  {
                 utx.rollback();
             catch  (IllegalStateException | SecurityException
                     | SystemException e1) {
                 e1.printStackTrace();
             }
         }
     }


 使用上述三种UserTransaction进行测试,其中test1方法是成功执行,test2方法是执行失败的(因为插入的值长度超过的字段长度限制)。通过分析之后,如果分布式事物控制正确,那么数据库中写入的值对于两张不同的表而言,是没有数字值被写入的。如图所示:

 wKiom1Vu_tiwRFwEAAHJhvqOEPQ597.jpg


 在测试过程中,经过对比确实达到了分布式事务控制的效果。

 整个操作的架构图如下(摘自Atomikos Blog)

 wKiom1VvoGrCpUisAAFjdTzZnG0617.jpg


 关于JTA原理文章开始提到的那篇文章,写的很详细和清晰,可以细细阅读和理解。



本文转自 secondriver 51CTO博客,原文链接:http://blog.51cto.com/aiilive/1658102,如需转载请自行联系原作者

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
4月前
|
存储 缓存 NoSQL
深入理解Django与Redis的集成实践
深入理解Django与Redis的集成实践
122 0
|
3月前
|
机器学习/深度学习 人工智能 jenkins
软件测试中的自动化与持续集成实践
在快速迭代的软件开发过程中,自动化测试和持续集成(CI)是确保代码质量和加速产品上市的关键。本文探讨了自动化测试的重要性、常见的自动化测试工具以及如何将自动化测试整合到持续集成流程中,以提高软件测试的效率和可靠性。通过案例分析,展示了自动化测试和持续集成在实际项目中的应用效果,并提供了实施建议。
|
3月前
|
jenkins Devops Java
DevOps实践:Jenkins在持续集成与持续部署中的价值
【10月更文挑战第27天】在快速发展的软件开发领域,DevOps实践日益重要。Jenkins作为一款流行的开源自动化服务器,在持续集成(CI)和持续部署(CD)中扮演关键角色。本文通过案例分析,探讨Jenkins在Java项目中的应用,展示其自动化构建、测试和部署的能力,提高开发效率和软件质量。
114 2
|
1月前
|
数据采集 人工智能 分布式计算
MaxFrame:链接大数据与AI的高效分布式计算框架深度评测与实践!
阿里云推出的MaxFrame是链接大数据与AI的分布式Python计算框架,提供类似Pandas的操作接口和分布式处理能力。本文从部署、功能验证到实际场景全面评测MaxFrame,涵盖分布式Pandas操作、大语言模型数据预处理及企业级应用。结果显示,MaxFrame在处理大规模数据时性能显著提升,代码兼容性强,适合从数据清洗到训练数据生成的全链路场景...
94 5
MaxFrame:链接大数据与AI的高效分布式计算框架深度评测与实践!
|
1月前
|
缓存 安全 Java
Spring Boot 3 集成 Spring Security + JWT
本文详细介绍了如何使用Spring Boot 3和Spring Security集成JWT,实现前后端分离的安全认证概述了从入门到引入数据库,再到使用JWT的完整流程。列举了项目中用到的关键依赖,如MyBatis-Plus、Hutool等。简要提及了系统配置表、部门表、字典表等表结构。使用Hutool-jwt工具类进行JWT校验。配置忽略路径、禁用CSRF、添加JWT校验过滤器等。实现登录接口,返回token等信息。
350 12
|
1月前
|
存储 运维 安全
盘古分布式存储系统的稳定性实践
本文介绍了阿里云飞天盘古分布式存储系统的稳定性实践。盘古作为阿里云的核心组件,支撑了阿里巴巴集团的众多业务,确保数据高可靠性、系统高可用性和安全生产运维是其关键目标。文章详细探讨了数据不丢不错、系统高可用性的实现方法,以及通过故障演练、自动化发布和健康检查等手段保障生产安全。总结指出,稳定性是一项系统工程,需要持续迭代演进,盘古经过十年以上的线上锤炼,积累了丰富的实践经验。
|
1月前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
82 8
|
2月前
|
DataWorks 数据挖掘 大数据
方案实践测评 | DataWorks集成Hologres构建一站式高性能的OLAP数据分析
DataWorks在任务开发便捷性、任务运行速度、产品使用门槛等方面都表现出色。在数据处理场景方面仍有改进和扩展的空间,通过引入更多的智能技术、扩展数据源支持、优化任务调度和可视化功能以及提升团队协作效率,DataWorks将能够为企业提供更全面、更高效的数据处理解决方案。
|
2月前
|
运维 Kubernetes 调度
阿里云容器服务 ACK One 分布式云容器企业落地实践
阿里云容器服务ACK提供强大的产品能力,支持弹性、调度、可观测、成本治理和安全合规。针对拥有IDC或三方资源的企业,ACK One分布式云容器平台能够有效解决资源管理、多云多集群管理及边缘计算等挑战,实现云上云下统一管理,提升业务效率与稳定性。
|
4月前
|
运维 监控 Devops
DevOps实践:自动化部署与持续集成的融合之旅
【10月更文挑战第7天】在软件开发领域,DevOps已成为一种文化和实践,它倡导开发(Dev)与运维(Ops)之间的协作与整合。本文将引导读者了解如何通过自动化部署和持续集成(CI)的实践来提升软件交付的速度和质量。我们将探讨一些实用的工具和技术,以及它们是如何帮助团队高效地管理代码变更、测试和部署的。文章将不包含代码示例,但会详细解释概念和流程,确保内容的通俗易懂和条理性。
170 62