springboot+druid+mybatis+mysql+多数据源事务管理

分布式事务在java中的解决方案就是JTA(即Java Transaction API);springboot官方提供了 Atomikos or Bitronix的解决思路;其实,大多数情况下很多公司是使用消息队列的方式实现分布式事务。这里分享的是Atomikos 的简单事务管理。

项目依赖

pom.xml中添加atomikos的springboot相关依赖:

  1. <!--分布式事务管理器-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-jta-atomikos</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>mysql</groupId>
  8. <artifactId>mysql-connector-java</artifactId>
  9. <!--这里最好要5.1.47之后的版本-->
  10. <version>5.1.47</version>
  11. </dependency>

application.properties配置文件中数据库相关信息:

  1. #数据库1
  2. spring.datasource.druid.one.url=jdbc:mysql://localhost:3306/test01?useUnicode=true&characterEncoding=utf-8
  3. spring.datasource.druid.one.username=root
  4. spring.datasource.druid.one.password=123456
  5. #数据库2

  6. spring.datasource.druid.two.url=jdbc:mysql://localhost:3306/test02?useUnicode=true&characterEncoding=utf-8

  7. spring.datasource.druid.two.username=root

  8. spring.datasource.druid.two.password=123456

创建两个java配置类,分别读取上面的两个数据库相关信息:

  1. @ConfigurationProperties(prefix = "spring.datasource.druid.one")
  2. public class DsOneProperties {
  3. private String username;
  4. private String password;
  5. private String url;
  6. //这里省掉Set和get方法
  7. }
  1. @ConfigurationProperties(prefix = "spring.datasource.druid.two")
  2. public class DsTwoProperties {
  3. private String username;
  4. private String password;
  5. private String url;
  6. //这里省掉Set和get方法
  7. }

在SpringBoot项目启动类加上注解,启动时,就加载相关信息

  1. @EnableConfigurationProperties(value = {DsOneProperties.class, DsTwoProperties.class})

创建主数据库配置类MyBatisConfigOne :

  1. @Configuration//声明该类是一个配置类
  2. @MapperScan(basePackages = "com.lwh.mybatistest.mapper", sqlSessionFactoryRef = "sqlSessionFactory1", sqlSessionTemplateRef = "sqlSessionTemplate1")
  3. //扫描的包是com.lwh.mybatistest.mapper
  4. public class MyBatisConfigOne {
  5. // 配置主数据源
  6. @Primary
  7. @Bean
  8. public DataSource dsOne(DsOneProperties dsOneProperties) throws SQLException {
  9. //配置XA协议数据源,从配置文件中读取相应属性
  10. MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
  11. mysqlXaDataSource.setUrl(dsOneProperties.getUrl());
  12. mysqlXaDataSource.setPassword(dsOneProperties.getPassword());
  13. mysqlXaDataSource.setUser(dsOneProperties.getUsername());
  14. //将本地事务注册到Atomikos全局事务
  15. AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
  16. xaDataSource.setXaDataSource(mysqlXaDataSource);
  17. xaDataSource.setUniqueResourceName("dsOne");
  18. return xaDataSource;
  19. }
  20. <span class="hljs-meta">@Primary</span>
  21. <span class="hljs-meta">@Bean</span>(name = <span class="hljs-string">"sqlSessionFactory1"</span>)
  22. <span class="hljs-function"><span class="hljs-keyword">public</span> SqlSessionFactory <span class="hljs-title">SqlSessionFactory1</span><span class="hljs-params">(@Qualifier(<span class="hljs-string">"dsOne"</span>)</span> DataSource dataSource)
  23.         <span class="hljs-keyword">throws</span> Exception </span>{
  24.     SqlSessionFactoryBean bean = <span class="hljs-keyword">new</span> SqlSessionFactoryBean();
  25.     bean.setDataSource(dataSource);
  26.     <span class="hljs-keyword">return</span> bean.getObject();
  27. }
  28. <span class="hljs-meta">@Primary</span>
  29. <span class="hljs-meta">@Bean</span>(name = <span class="hljs-string">"sqlSessionTemplate1"</span>)
  30. <span class="hljs-function"><span class="hljs-keyword">public</span> SqlSessionTemplate <span class="hljs-title">SqlSessionTemplate1</span><span class="hljs-params">(
  31.         @Qualifier(<span class="hljs-string">"sqlSessionFactory1"</span>)</span> SqlSessionFactory sqlSessionFactory) <span class="hljs-keyword">throws</span> Exception </span>{
  32.     <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> SqlSessionTemplate(sqlSessionFactory);
  33. }
  34. }

依照上面主数据库配置类,创建从数据库配置类:

  1. @Configuration
  2. @MapperScan(basePackages = "com.lwh.mybatistest.mapper2", sqlSessionFactoryRef = "sqlSessionFactory2", sqlSessionTemplateRef = "sqlSessionTemplate2")
  3. public class MyBatisConfigTwo {
  4. @Bean
  5. public DataSource dsTwo(DsTwoProperties dsTwoProperties) throws SQLException {
  6. //配置从数据源
  7. //配置XA协议数据源,从配置文件中读取相应属性
  8. MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
  9. mysqlXaDataSource.setUrl(dsTwoProperties.getUrl());
  10. mysqlXaDataSource.setPassword(dsTwoProperties.getPassword());
  11. mysqlXaDataSource.setUser(dsTwoProperties.getUsername());
  12. //将本地事务注册到Atomikos全局事务
  13. AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
  14. xaDataSource.setXaDataSource(mysqlXaDataSource);
  15. xaDataSource.setUniqueResourceName("dsTwo");
  16. return xaDataSource;
  17. }
  18. <span class="hljs-meta">@Bean</span>(name = <span class="hljs-string">"sqlSessionFactory2"</span>)
  19. <span class="hljs-function"><span class="hljs-keyword">public</span> SqlSessionFactory <span class="hljs-title">SqlSessionFactory2</span><span class="hljs-params">(@Qualifier(<span class="hljs-string">"dsTwo"</span>)</span> DataSource dataSource)
  20.         <span class="hljs-keyword">throws</span> Exception </span>{
  21.     SqlSessionFactoryBean bean = <span class="hljs-keyword">new</span> SqlSessionFactoryBean();
  22.     bean.setDataSource(dataSource);
  23.     <span class="hljs-keyword">return</span> bean.getObject();
  24. }
  25. <span class="hljs-meta">@Bean</span>(name = <span class="hljs-string">"sqlSessionTemplate2"</span>)
  26. <span class="hljs-function"><span class="hljs-keyword">public</span> SqlSessionTemplate <span class="hljs-title">SqlSessionTemplate2</span><span class="hljs-params">(
  27.         @Qualifier(<span class="hljs-string">"sqlSessionFactory2"</span>)</span> SqlSessionFactory sqlSessionFactory) <span class="hljs-keyword">throws</span> Exception </span>{
  28.     <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> SqlSessionTemplate(sqlSessionFactory);
  29. }
  30. }

创建一个简单的controller测试类:

  1. @RestController
  2. @RequestMapping("/book")
  3. public class BookController {
  4. @Autowired
  5. BookService bookService;
  6. <span class="hljs-meta">@Autowired</span>
  7. BookService2 bookService2;
  8. <span class="hljs-meta">@GetMapping</span>(<span class="hljs-string">"/add1"</span>)
  9. <span class="hljs-meta">@Transactional</span>
  10. <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">addBook</span><span class="hljs-params">()</span> </span>{
  11.     Book book = <span class="hljs-keyword">new</span> Book();
  12.     book.setBookname(<span class="hljs-string">"测试"</span>);
  13.     book.setAuthor(<span class="hljs-string">"test:01"</span>);
  14.     System.out.println(<span class="hljs-string">"数据库1:&gt;&gt;&gt;&gt;"</span>);
  15.     bookService.addBook(book);
  16.     System.out.println(<span class="hljs-string">"数据库2:&gt;&gt;&gt;&gt;"</span>);
  17.     bookService2.addBook(book);
  18.     <span class="hljs-keyword">return</span> <span class="hljs-string">"测试add1操作成功!"</span>;
  19. }
  20. <span class="hljs-meta">@GetMapping</span>(<span class="hljs-string">"/add2"</span>)
  21. <span class="hljs-meta">@Transactional</span>
  22. <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">addBook2</span><span class="hljs-params">()</span> </span>{
  23.     Book book = <span class="hljs-keyword">new</span> Book();
  24.     book.setBookname(<span class="hljs-string">"测试add2"</span>);
  25.     book.setAuthor(<span class="hljs-string">"test:01"</span>);
  26.     System.out.println(<span class="hljs-string">"数据库1:&gt;&gt;&gt;&gt;"</span>);
  27.     bookService.addBook(book);
  28.     <span class="hljs-keyword">int</span> a = <span class="hljs-number">10</span> / <span class="hljs-number">0</span>;
  29.     System.out.println(<span class="hljs-string">"数据库2:&gt;&gt;&gt;&gt;"</span>);
  30.     bookService2.addBook(book);
  31.     <span class="hljs-keyword">return</span> <span class="hljs-string">"测试add2操作成功!"</span>;
  32. }
  33. }

Service类,就是简单的插入方法,调用mapper:

  1. @Service
  2. public class BookService {
  3. <span class="hljs-meta">@Autowired</span>
  4. BookMapper bookMapper;
  5. <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addBook</span><span class="hljs-params">(Book book)</span> </span>{
  6.     bookMapper.insertSelective(book);
  7. }
  8. }

springboot默认有事务管理器,所以这里没有配置,使用默认的即可,如果有特别需求,可以自行创建自己的事务管理器。

最简单的atomikos插件的使用就配置完了,配置信息相对简单,想深入学习的同学,可以参考官方的文档。

分布式事务有多种主流形态,包括:

基于消息实现的分布式事务

基于补偿实现的分布式事务(gts/seata自动补偿的形式)

基于TCC实现的分布式事务

基于SAGA实现的分布式事务

基于2PC实现的分布式事务

之所以有这么多形态,是因为任何事情都没有银弹,只有最合适当前场景的解决方案。

springbootdruidmybatismysql多数据源事务管理的更多相关文章

  1. spring+springmvc+mybatis+oracle+atomikos+jta实现多数据源事务管理

    ---恢复内容开始---   在做项目过程中,遇到了需要一个项目中访问两个数据库的情况,发现使用常规的spring管理事务,导致事务不能正常回滚,因此,采用了jta+atomikos的分布式数据源方式 ...

  2. springboot-jta-atomikos多数据源事务管理

    背景 我们平时在用springboot开发时,要使用事务,只需要在方法上添加@Transaction注解即可,但这种方式只适用单数据源,在多数据源下就不再适用: 比如在多数据源下,我们在一个方法里执行 ...

  3. Spring中实现多数据源事务管理

    文章转自  https://www.2cto.com/kf/201507/424229.html 前言 由于项目中引入了多个数据源,并且需要对多个数据源进行写操作,那么多数据源的事务管理自然成了不可避 ...

  4. Spring Boot2.0之多数据源事务管理

    结合前面做的小项目,如果我把test01 test02下面的 service 都加了 事务的注解 这样启动时候会报错! 事务管理器里面不能有两个事务!!!! 这时候需要用  @Transactiona ...

  5. 二、spring集成ibatis进行数据源事务管理拦截器环境配置

    1.dataSource-applicationContext.xml文件配置理解:(spring1.2.8+ibatis1.5.3)1.1)配置数据源 DriverManagerDataSource ...

  6. SpringBoot2 整合JTA组件,多数据源事务管理

    本文源码:GitHub·点这里 || GitEE·点这里 一.JTA组件简介 1.JTA基本概念 JTA即Java-Transaction-API,JTA允许应用程序执行分布式事务处理,即在两个或多个 ...

  7. spring JTA多数据源事务管理详细教程

    <context:annotation-config /> <!-- 使用注解的包路径 --> <context:component-scan base-package= ...

  8. spring boot 或 spring 集成 atomikos jta 完成多数据源事务管理

    前言:对于事务,spring 不提供自己的实现,只是定义了一个接口来供其他厂商实现,具体些的请看我的这篇文章: https://www.cnblogs.com/qiaoyutao/p/11289996 ...

  9. [转]Spring3核心技术之事务管理机制

    原文地址:http://chouyi.iteye.com/blog/1675199 Spring对事务的解决办法其实分为2种:编程式实现事务,AOP配置声明式解决方案. http://jinnians ...

随机推荐

  1. 关于springboot的日志logging.file和logging.path的配置问题

    springboot日志配置 logging.path  logging.file 它们俩不会同时生效,so只配置其中一个就好了. eg1: 单独一个path配置 logging.path=E:/lo ...

  2. Windows安全日志

    在运行中输入:eventvwr.msc,即可打开事件日志. 登录类型 描述 2 互动(键盘和屏幕的登录系统) 3 网络(即连接到共享文件夹从其他地方在这台电脑上网络) 4 批处理(即计划任务) 5 服 ...

  3. 【Trie】Phone List

    [题目链接]: https://loj.ac/problem/10049 [题意] 问是否存在一组公共前缀.如果存在输出“NO”,否则输出“YES” [题解] 首先建出Trie树来,然后开始记录所有的 ...

  4. 怎样获取页面中所有带href属性的标签集合

    使用: document.links document.links instanceof HTMLCollection; 注意: 1. a 标签和 area 标签可以设置 href属性, 因此可以被获 ...

  5. Python实现字符的冒泡排序——说实话,两个数兑换的方法震惊了我,一天比一天感受到了Python的强大

    import random M= lettList=[] for i in range(M): lettList.append(chr(random.randrange(,))) for lett i ...

  6. netty 自定义协议

    netty 自定义协议 netty 是什么呢? 相信很多人都被人问过这个问题.如果快速准确的回复这个问题呢?网络编程框架,netty可以让你快速和简单的开发出一个高性能的网络应用.netty是一个网络 ...

  7. LeetCode 腾讯精选50题-- 买卖股票的最佳时机 II

    贪心算法: 具体的解题思路如下: II 的解题思路可以分为两部分, 1. 找到数组中差值较大的两个元素,计算差值. 2. 再步骤一最大的元素的之后,继续遍历,寻找差值最大的两个元素 可以得出的是,遍历 ...

  8. Caffe之layer_factory

    之前在测试NN中各个层的时间的时候,遇到一个非常奇怪的问题,分别使用Caffe自己的gpu方法和cuDNN方法,在卷积上性能差异非常大,但是在pooling层上基本没有变化.抽空检查了代码之后,发现是 ...

  9. vue项目js实现图片放大镜功能

    效果图:   我写的是vue的组件形式,方便复用,图片的宽高,缩放的比例可以自己定义 magnifier.vue <template> <div class="magnif ...

  10. SQL脚本优化

    1.创建索引一.要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引   (1)在经常需要进行检索的字段上创建索引,比如要按照表字段username进行检索,那么就应 ...