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

  1. DROP DATABASE IF EXISTS `datasource1`;
  2. CREATE DATABASE `datasource1` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
  3.  
  4. use datasource1;
  5.  
  6. DROP TABLE IF EXISTS `orders`;
  7. CREATE TABLE `orders` (
  8. `id` int(11) NOT NULL AUTO_INCREMENT,
  9. `code` int(11) DEFAULT NULL,
  10. `quantity` int(11) DEFAULT NULL,
  11. PRIMARY KEY (`id`)
  12. ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
  13.  
  14. DROP DATABASE IF EXISTS `datasource2`;
  15. CREATE DATABASE `datasource2` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
  16.  
  17. use datasource2;
  18.  
  19. DROP TABLE IF EXISTS `customer`;
  20. CREATE TABLE `customer` (
  21. `id` int(11) NOT NULL AUTO_INCREMENT,
  22. `name` varchar(45) DEFAULT NULL,
  23. `age` int(11) DEFAULT NULL,
  24. PRIMARY KEY (`id`)
  25. ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

3.2 部分重要代码

  pom.xml

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-data-jpa</artifactId>
  5. </dependency>
  6.  
  7. <!-- <dependency>
  8. <groupId>com.h2database</groupId>
  9. <artifactId>h2</artifactId>
  10. </dependency> -->
  11.  
  12. <dependency>
  13. <groupId>org.projectlombok</groupId>
  14. <artifactId>lombok</artifactId>
  15. <version>1.12.4</version>
  16. </dependency>
  17.  
  18. <dependency>
  19. <groupId>com.atomikos</groupId>
  20. <artifactId>transactions</artifactId>
  21. <version>3.9.3</version>
  22. </dependency>
  23.  
  24. <dependency>
  25. <groupId>com.atomikos</groupId>
  26. <artifactId>transactions-jta</artifactId>
  27. <version>3.9.3</version>
  28. </dependency>
  29.  
  30. <dependency>
  31. <groupId>com.atomikos</groupId>
  32. <artifactId>transactions-hibernate3</artifactId>
  33. <version>3.9.3</version>
  34. <exclusions>
  35. <exclusion>
  36. <artifactId>hibernate</artifactId>
  37. <groupId>org.hibernate</groupId>
  38. </exclusion>
  39. </exclusions>
  40. </dependency>
  41.  
  42. <dependency>
  43. <groupId>org.springframework.boot</groupId>
  44. <artifactId>spring-boot-starter-test</artifactId>
  45. </dependency>
  46.  
  47. <dependency>
  48. <groupId>junit</groupId>
  49. <artifactId>junit</artifactId>
  50. <scope>test</scope>
  51. </dependency>
  52.  
  53. <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
  54. <dependency>
  55. <groupId>mysql</groupId>
  56. <artifactId>mysql-connector-java</artifactId>
  57. <version>5.1.31</version>
  58. </dependency>
  59.  
  60. </dependencies>

application.properties

  1. spring.main.show_banner=false
  2.  
  3. order.datasource.url=jdbc:mysql://192.168.0.12:3306/datasource1?serverTimezone=UTC
  4. order.datasource.user=root
  5. order.datasource.password=123456
  6. #jdbc:h2:order
  7.  
  8. customer.datasource.url=jdbc:mysql://127.0.0.1:3312/datasource2?serverTimezone=UTC
  9. customer.datasource.user=root
  10. customer.datasource.password=123456
  1. import java.util.HashMap;
  2.  
  3. import javax.sql.DataSource;
  4.  
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.boot.context.properties.EnableConfigurationProperties;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.context.annotation.DependsOn;
  10. import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  11. import org.springframework.orm.jpa.JpaVendorAdapter;
  12. import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
  13.  
  14. import com.at.mul.repository.customer.CustomerDatasourceProperties;
  15. import com.atomikos.jdbc.AtomikosDataSourceBean;
  16. import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
  17.  
  18. @Configuration
  19. @DependsOn("transactionManager")
  20. @EnableJpaRepositories(basePackages = "com.at.mul.repository.customer", entityManagerFactoryRef = "customerEntityManager", transactionManagerRef = "transactionManager")
  21. @EnableConfigurationProperties(CustomerDatasourceProperties.class)
  22. public class CustomerConfig {
  23.  
  24. @Autowired
  25. private JpaVendorAdapter jpaVendorAdapter;
  26.  
  27. @Autowired
  28. private CustomerDatasourceProperties customerDatasourceProperties;
  29.  
  30. @Bean(name = "customerDataSource", initMethod = "init", destroyMethod = "close")
  31. public DataSource customerDataSource() {
  32. MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
  33. mysqlXaDataSource.setURL(customerDatasourceProperties.getUrl());
  34. mysqlXaDataSource.setUser(customerDatasourceProperties.getUser());
  35. mysqlXaDataSource.setPassword(customerDatasourceProperties.getPassword());
  36. mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
  37.  
  38. AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
  39. xaDataSource.setXaDataSource(mysqlXaDataSource);
  40. xaDataSource.setUniqueResourceName("datasource2");
  41. // xaDataSource.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
  42. return xaDataSource;
  43. }
  44.  
  45. @Bean(name = "customerEntityManager")
  46. @DependsOn("transactionManager")
  47. public LocalContainerEntityManagerFactoryBean customerEntityManager() throws Throwable {
  48.  
  49. HashMap<String, Object> properties = new HashMap<String, Object>();
  50. properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
  51. properties.put("javax.persistence.transactionType", "JTA");
  52.  
  53. LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
  54. entityManager.setJtaDataSource(customerDataSource());
  55. entityManager.setJpaVendorAdapter(jpaVendorAdapter);
  56. entityManager.setPackagesToScan("com.at.mul.domain.customer");
  57. entityManager.setPersistenceUnitName("customerPersistenceUnit");
  58. entityManager.setJpaPropertyMap(properties);
  59. return entityManager;
  60. }
  61.  
  62. }
  1. @Configuration
  2. @DependsOn("transactionManager")
  3. @EnableJpaRepositories(basePackages = "com.at.mul.repository.order", entityManagerFactoryRef = "orderEntityManager", transactionManagerRef = "transactionManager")
  4. @EnableConfigurationProperties(OrderDatasourceProperties.class)
  5. public class OrderConfig {
  6.  
  7. @Autowired
  8. private JpaVendorAdapter jpaVendorAdapter;
  9.  
  10. @Autowired
  11. private OrderDatasourceProperties orderDatasourceProperties;
  12.  
  13. @Bean(name = "orderDataSource", initMethod = "init", destroyMethod = "close")
  14. public DataSource orderDataSource() {
  15. MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
  16. mysqlXaDataSource.setURL(orderDatasourceProperties.getUrl());
  17. mysqlXaDataSource.setUser(orderDatasourceProperties.getUser());
  18. mysqlXaDataSource.setPassword(orderDatasourceProperties.getPassword());
  19. // mysqlXaDataSource.setAllowMultiQueries(true);
  20. // mysqlXaDataSource.setLogXaCommands(true);
  21. mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
  22.  
  23. AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
  24. xaDataSource.setXaDataSource(mysqlXaDataSource);
  25. xaDataSource.setUniqueResourceName("datasource1");
  26. // xaDataSource.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
  27. return xaDataSource;
  28. }
  29.  
  30. @Bean(name = "orderEntityManager")
  31. public LocalContainerEntityManagerFactoryBean orderEntityManager() throws Throwable {
  32.  
  33. HashMap<String, Object> properties = new HashMap<String, Object>();
  34. properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
  35. properties.put("javax.persistence.transactionType", "JTA");
  36.  
  37. LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
  38. entityManager.setJtaDataSource(orderDataSource());
  39. entityManager.setJpaVendorAdapter(jpaVendorAdapter);
  40. entityManager.setPackagesToScan("com.at.mul.domain.order");
  41. entityManager.setPersistenceUnitName("orderPersistenceUnit");
  42. entityManager.setJpaPropertyMap(properties);
  43. return entityManager;
  44. }
  45.  
  46. }
  1. import com.at.mul.domain.customer.Customer;
  2. import com.at.mul.domain.order.Order;
  3. import com.at.mul.exception.NoRollbackException;
  4. import com.at.mul.exception.StoreException;
  5.  
  6. public interface StoreService {
  7.  
  8. void store(Customer customer, Order order) throws Exception;
  9.  
  10. void storeWithStoreException(Customer customer, Order order) throws StoreException;
  11.  
  12. void storeWithNoRollbackException(Customer customer, Order order) throws NoRollbackException;
  13.  
  14. }
  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.stereotype.Service;
  3. import org.springframework.transaction.annotation.Transactional;
  4.  
  5. import com.at.mul.domain.customer.Customer;
  6. import com.at.mul.domain.order.Order;
  7. import com.at.mul.exception.NoRollbackException;
  8. import com.at.mul.exception.StoreException;
  9. import com.at.mul.repository.customer.CustomerRepository;
  10. import com.at.mul.repository.order.OrderRepository;
  11.  
  12. @Service
  13. public class StoreServiceImpl implements StoreService {
  14.  
  15. @Autowired
  16. private CustomerRepository customerRepository;
  17.  
  18. @Autowired
  19. private OrderRepository orderRepository;
  20.  
  21. @Transactional
  22. public void store(Customer customer, Order order) {
  23. customerRepository.save(customer);
  24. orderRepository.save(order);
  25. }
  26.  
  27. @Transactional(rollbackFor = StoreException.class)
  28. public void storeWithStoreException(Customer customer, Order order) throws StoreException {
  29. customerRepository.save(customer);
  30. orderRepository.save(order);
  31. throw new StoreException();
  32. }
  33.  
  34. @Transactional(noRollbackFor = NoRollbackException.class, rollbackFor = StoreException.class)
  35. public void storeWithNoRollbackException(Customer customer, Order order) throws NoRollbackException {
  36. customerRepository.save(customer);
  37. orderRepository.save(order);
  38. throw new NoRollbackException();
  39. }
  40.  
  41. }

完整代码下载: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
找到下面的段落

  1. MySQL XA bug
  2. 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:
  3. com.atomikos.icatch.serial_jta_transactions=false
  4. Also, make sure to set the following property on the MySQL datasource:
  5. pinGlobalTxToPhysicalConnection="true"
  6. 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. Leetcode_122_Best Time to Buy and Sell Stock II

    本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/43155725 Say you have an array ...

  2. ra_interface_lines_all 接口表各字段说明

    note:Description and Usage of Fields in RA_INTERFACE_LINES Table [ID 1195997.1] 核心内容: Field Name and ...

  3. Github 错误合集:Failed connect to github.com:8080 || Failed connect to github.com:443; No error

    文/skay 地址:http://blog.csdn.net/sk719887916/article/details/40541199 开发中遇到github无法pull和push代码问题,原来git ...

  4. jQuery中常用的函数方法总结

    jQuery中为我们提供了很多有用的方法和属性,自己总结的一些常用的函数,方法.个人认为在开发中会比较常用的,仅供大家学习和参考. 事件处理 ready(fn) 代码: $(document).rea ...

  5. Android高效率编码-细节,控件,架包,功能,工具,开源汇总,你想要的这里都有

    Android高效率编码-细节,控件,架包,功能,工具,开源汇总 其实写博客的初衷也并不是说什么分享技术,毕竟咱还只是个小程序员,最大的目的就是对自我的知识积累,以后万一编码的时候断片了,也可以翻出来 ...

  6. 从开发者角度解析 Android N 新特性!

    大清早看到 Google 官方博客发布 Android N 的开发者预览版,立马从床上跳起来开始仔仔细细的读起来. 从开发者角度来看,Android N 的更新并不算大.网上之前流传的一些 Andro ...

  7. LeetCode(31)-Factorial Trailing Zeroes

    题目: Given an integer n, return the number of trailing zeroes in n!. Note: Your solution should be in ...

  8. XML学习教程

    XML学习进阶1-- 什么是XML. 为什么使用 XML?... 什么是 XML?... 数据的结构表示... XML 文档... 数据是从表示和处理中分离出来的... 使XML数据自描述... XM ...

  9. java原子操作

    一.何谓Atomic? Atomic一词跟原子有点关系,后者曾被人认为是最小物质的单位.计算机中的Atomic是指不能分割成若干部分的意思.如果一段代码被认为是Atomic,则表示这段代码在执行过程中 ...

  10. SQL 逻辑优化 case when 转为 union all

    通常数据库的优化从硬件层面去考虑可分为4个方面: CPU:即降低计算复杂度,如减少sql各类聚合函数,窗口函数,case when等. IO :(较少查询结果集过程中对数据的访问量.数据优化很大程度从 ...