1 前言

之前整理了一个spring+jotm实现的分布式事务实现,但是听说spring3.X后不再支持jotm了,jotm也有好几年没更新了,所以今天整理springboot+Atomikos+jpa+mysql的JTA分布式事务实现。

Atomikos网上的资料确实比jotm多,另外我发现STS工具里集成了Atomikos,那spring对Atomikos的支持毋庸置疑肯定会在相当长的时间内会是友好的。

2 开发环境

Springboot 1.0.1 + Atomikos 3.9.3 + JPA (Hibernate 4.3.5) + Mysql 5.1.73 + Mysql Connector 5.1.31 + Junit + Maven

3 代码

这套代码的基础我是从网上下载的,作了些修改,因为它原来用的是H2数据库,我改成了Mysql,另外,其实spring官网的例子也是这样写的,http://spring.io/blog/2011/08/15/configuring-spring-and-jta-without-full-java-ee/

3.1 数据库sql

 DROP DATABASE IF EXISTS `datasource1`;
CREATE DATABASE `datasource1` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; use datasource1; DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` int(11) DEFAULT NULL,
`quantity` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8; DROP DATABASE IF EXISTS `datasource2`;
CREATE DATABASE `datasource2` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; use datasource2; DROP TABLE IF EXISTS `customer`;
CREATE TABLE `customer` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

3.2 部分重要代码

  pom.xml

 <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency> <!-- <dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency> --> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.12.4</version>
</dependency> <dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions</artifactId>
<version>3.9.3</version>
</dependency> <dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jta</artifactId>
<version>3.9.3</version>
</dependency> <dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-hibernate3</artifactId>
<version>3.9.3</version>
<exclusions>
<exclusion>
<artifactId>hibernate</artifactId>
<groupId>org.hibernate</groupId>
</exclusion>
</exclusions>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.31</version>
</dependency> </dependencies>

application.properties

 spring.main.show_banner=false

 order.datasource.url=jdbc:mysql://192.168.0.12:3306/datasource1?serverTimezone=UTC
order.datasource.user=root
order.datasource.password=123456
#jdbc:h2:order customer.datasource.url=jdbc:mysql://127.0.0.1:3312/datasource2?serverTimezone=UTC
customer.datasource.user=root
customer.datasource.password=123456
 import java.util.HashMap;

 import javax.sql.DataSource;

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import com.at.mul.repository.customer.CustomerDatasourceProperties;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; @Configuration
@DependsOn("transactionManager")
@EnableJpaRepositories(basePackages = "com.at.mul.repository.customer", entityManagerFactoryRef = "customerEntityManager", transactionManagerRef = "transactionManager")
@EnableConfigurationProperties(CustomerDatasourceProperties.class)
public class CustomerConfig { @Autowired
private JpaVendorAdapter jpaVendorAdapter; @Autowired
private CustomerDatasourceProperties customerDatasourceProperties; @Bean(name = "customerDataSource", initMethod = "init", destroyMethod = "close")
public DataSource customerDataSource() {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setURL(customerDatasourceProperties.getUrl());
mysqlXaDataSource.setUser(customerDatasourceProperties.getUser());
mysqlXaDataSource.setPassword(customerDatasourceProperties.getPassword());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("datasource2");
// xaDataSource.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
return xaDataSource;
} @Bean(name = "customerEntityManager")
@DependsOn("transactionManager")
public LocalContainerEntityManagerFactoryBean customerEntityManager() throws Throwable { HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
properties.put("javax.persistence.transactionType", "JTA"); LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
entityManager.setJtaDataSource(customerDataSource());
entityManager.setJpaVendorAdapter(jpaVendorAdapter);
entityManager.setPackagesToScan("com.at.mul.domain.customer");
entityManager.setPersistenceUnitName("customerPersistenceUnit");
entityManager.setJpaPropertyMap(properties);
return entityManager;
} }
 @Configuration
@DependsOn("transactionManager")
@EnableJpaRepositories(basePackages = "com.at.mul.repository.order", entityManagerFactoryRef = "orderEntityManager", transactionManagerRef = "transactionManager")
@EnableConfigurationProperties(OrderDatasourceProperties.class)
public class OrderConfig { @Autowired
private JpaVendorAdapter jpaVendorAdapter; @Autowired
private OrderDatasourceProperties orderDatasourceProperties; @Bean(name = "orderDataSource", initMethod = "init", destroyMethod = "close")
public DataSource orderDataSource() {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setURL(orderDatasourceProperties.getUrl());
mysqlXaDataSource.setUser(orderDatasourceProperties.getUser());
mysqlXaDataSource.setPassword(orderDatasourceProperties.getPassword());
// mysqlXaDataSource.setAllowMultiQueries(true);
// mysqlXaDataSource.setLogXaCommands(true);
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("datasource1");
// xaDataSource.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
return xaDataSource;
} @Bean(name = "orderEntityManager")
public LocalContainerEntityManagerFactoryBean orderEntityManager() throws Throwable { HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
properties.put("javax.persistence.transactionType", "JTA"); LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
entityManager.setJtaDataSource(orderDataSource());
entityManager.setJpaVendorAdapter(jpaVendorAdapter);
entityManager.setPackagesToScan("com.at.mul.domain.order");
entityManager.setPersistenceUnitName("orderPersistenceUnit");
entityManager.setJpaPropertyMap(properties);
return entityManager;
} }
 import com.at.mul.domain.customer.Customer;
import com.at.mul.domain.order.Order;
import com.at.mul.exception.NoRollbackException;
import com.at.mul.exception.StoreException; public interface StoreService { void store(Customer customer, Order order) throws Exception; void storeWithStoreException(Customer customer, Order order) throws StoreException; void storeWithNoRollbackException(Customer customer, Order order) throws NoRollbackException; }
 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import com.at.mul.domain.customer.Customer;
import com.at.mul.domain.order.Order;
import com.at.mul.exception.NoRollbackException;
import com.at.mul.exception.StoreException;
import com.at.mul.repository.customer.CustomerRepository;
import com.at.mul.repository.order.OrderRepository; @Service
public class StoreServiceImpl implements StoreService { @Autowired
private CustomerRepository customerRepository; @Autowired
private OrderRepository orderRepository; @Transactional
public void store(Customer customer, Order order) {
customerRepository.save(customer);
orderRepository.save(order);
} @Transactional(rollbackFor = StoreException.class)
public void storeWithStoreException(Customer customer, Order order) throws StoreException {
customerRepository.save(customer);
orderRepository.save(order);
throw new StoreException();
} @Transactional(noRollbackFor = NoRollbackException.class, rollbackFor = StoreException.class)
public void storeWithNoRollbackException(Customer customer, Order order) throws NoRollbackException {
customerRepository.save(customer);
orderRepository.save(order);
throw new NoRollbackException();
} }

完整代码下载:http://download.csdn.net/download/u013081610/9927514

4 遇到的坑

4.1 bug: The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone.

http://blog.csdn.net/sunlggggg/article/details/54564114

4.2 com.mysql.jdbc.jdbc2.optional.MysqlXAException: XAER_INVAL: Invalid arguments (or unsupported command)
WARNING: XA resource 'jdbc/mysqlDs': resume for XID '3139322E3136382E31342E3131372E746D30303030323030303831:3139322E3136382E31342E3131372E746D32' raised -5: invalid arguments were given for the XA operation

这个错误是我运行StoreServiceTest里的testStore()方法时出现的,就是把数据分别插入两个库的表里,之前用H2的时候都很正常,但是换成Mysql就是不行,操作第二个库的时候就报这个错,第一个不会报错。

猜测可能是以下原因吧
a.这可能是MySQL服务器对XA支持的限制,也就是可能是MySQL的一个bug,可以看Mysql官方文档的解释https://dev.mysql.com/doc/refman/5.5/en/xa-statements.html
b.也可能是atomikos里的问题,具体看https://www.atomikos.com/Documentation/KnownProblems#ActiveMQ_error:_34Transaction_39XID:..._39_has_not_been_started_34
找到下面的段落

MySQL XA bug
Some users have reported problems with MySQL XA (related to this MySQL bug: http://bugs.mysql.com/bug.php?id=27832external). This problem only happens if you access the same MySQL database more than once in the same transaction. A workaround can be setting the following property in jta.properties:
com.atomikos.icatch.serial_jta_transactions=false
Also, make sure to set the following property on the MySQL datasource:
pinGlobalTxToPhysicalConnection="true"
MariaDB's java driver also supports this workaround since v.1.1.8

看来atomikos已经针对mysql的这个bug作了处理了,根据提示我加了mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true)就可以了。

4.3 貌似对mysql的InnoDB引擎没用

具体什么原因我还没研究,暂时测试只是使用的MyISAM

Springboot+Atomikos+Jpa+Mysql实现JTA分布式事务的更多相关文章

  1. Springboot + Atomikos + Druid + Mysql 实现JTA分布式事务

    DataSource 配置 package com.cheng.dynamic.config; import java.util.Properties; import javax.sql.DataSo ...

  2. spring+jotm+ibatis+mysql实现JTA分布式事务

    1 环境 1.1 软件环境  spring-framework-2.5.6.SEC01-with-dependencies.zip ibatis-2.3.4 ow2-jotm-dist-2.1.4-b ...

  3. SpringBoot 使用JPA+MySQL+Thymeleaf 总结 二

    SpringBoot 使用JPA+MySQL+Thymeleaf 总结 一 SpringBoot 使用JPA+MySQL+Thymeleaf 总结 二 方法一 使用原生sql查询 或者 为方法名增加 ...

  4. SpringBoot 使用JPA+MySQL+Thymeleaf 总结 一

    SpringBoot 使用JPA+MySQL+Thymeleaf 总结 一 SpringBoot 使用JPA+MySQL+Thymeleaf 总结 二 pom引用 <?xml version=& ...

  5. springboot学习笔记:10.springboot+atomikos+mysql+mybatis+druid+分布式事务

    前言 上一篇文章我们整合了springboot+druid+mybatis+mysql+多数据源: 本篇文章大家主要跟随你们涛兄在上一届基础上配置一下多数据源情况下的分布式事务: 首先,到底啥是分布式 ...

  6. 使用Atomikos Transactions Essentials实现多数据源JTA分布式事务--转载

    原文:http://www.ite/topic/122700 9.17 update:使用NonXADataSourceBean. Mysql在5.0版本和Connecter/J5.0版本后提供了XA ...

  7. Spring 3.0 + Atomikos构建jta分布式事务

    Spring3.0已经不再支持jtom了,不过我们可以用第三方开源软件atomikos(http://www.atomikos.com/)来实现.Atomikos是目前在分布式事务管理中做得相当不错的 ...

  8. atomikos实现多数据源支持分布式事务管理(spring、tomcat、JTA)

    原文链接:http://iteye.blog.163.com/blog/static/1863080962012102945116222/   Atomikos TransactionsEssenti ...

  9. JTA 分布式事务

    什么是JTA - 2009-07-25 18:31:06|  分类: 技术文章|举报|字号 订阅     什么是JTA? Java Transaction API(Java事务API) (JTA)Ja ...

随机推荐

  1. 菜鸟玩云计算之廿一: saltstack之pillar

    菜鸟玩云计算之廿一: saltstack之pillar 参考: 点击打开链接 查看pillar数据: # salt '*' pillar.items pillar的默认根目录在:/srv/pillar ...

  2. OpenCV中OpenMP的使用

    vs2010中调用openMP,并添加头文件#include<omp.h> 代码来源: 作者:gnuhpc 出处:http://www.cnblogs.com/gnuhpc/ #inclu ...

  3. 网络I/O中的同步、异步、阻塞和非阻塞概念

    在学习网络编程过程中,经常会把这几个概念搞混淆. 同步I/O与异步I/O区别 我们先来看一下操作I/O时涉及的对象和步骤(这里我们以read为例): 这里会涉及到两个系统对象,一个是调用这个I/O的应 ...

  4. StarUML配置Word生成文档模板

    来源:fasiondog 许多UML建模工具可以自动生成文档,让需求人员.开发人员专心于需求.设计的建模.当然为了能够生成符合自己要求的模板,需对建模时的目录结构(模型和包)有所规划和要求,否则很难生 ...

  5. 销售订单-修改量-高级定价关联sql

    修改量消耗明细 --修改量消耗明细 SELECT t.name, t.comments, t.version_no, cux_rebate_pub.get_hou_name(p_organizatio ...

  6. jvm内存查看与分析工具

    2.3 JVM的垃圾收集策略   GC的执行时要耗费一定的CPU资源和时间的,因此在JDK1.2以后,JVM引入了分代收集的策略,其中对新生代采用"Mark-Compact"策略, ...

  7. Log4j运用于代码中

    在JAVA代码中,我们要打印输出语句的时候,我们经常会使用System.out.print(),但是在项目开发完后,这些代码就会影响项目的运行效率,所以Log4j就派上用场了.话不多说,直接上代码. ...

  8. 和菜鸟一起学linux之dlna的学习记录

    关于DLNA框架 1.Networking & Connectivity 为了解决物理设备连通问题, 主要依赖于Ethernet,802.11,Ipv4协议栈,Ipv6协议栈. TCP/IP协 ...

  9. PLSQL 创建自定义函数注意事项

    2017-6-8周四,今天遇到的需求是,从数据库中查找出某张表的某些数据,并将这些数据做简单的加减运算再得到结果集,没有思路,后来问辉哥,给我的建议是给这些运算封装成一个SQL函数,select选择字 ...

  10. 集群中几种session同步解决方案的比较[转]

    集群中session安全和同步是个最大的问题,下面是我收集到的几种session同步的方案,希望能通过分析其各自的优劣找出其适应的场景. 1. 客户端cookie加密 这是我以前采用的方式,简单,高效 ...