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

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

项目依赖

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

<!--分布式事务管理器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!--这里最好要5.1.47之后的版本-->
<version>5.1.47</version>
</dependency>

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

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

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

spring.datasource.druid.two.username=root

spring.datasource.druid.two.password=123456

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

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

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

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

创建主数据库配置类MyBatisConfigOne :

@Configuration//声明该类是一个配置类
@MapperScan(basePackages = "com.lwh.mybatistest.mapper", sqlSessionFactoryRef = "sqlSessionFactory1", sqlSessionTemplateRef = "sqlSessionTemplate1")
//扫描的包是com.lwh.mybatistest.mapper
public class MyBatisConfigOne {
// 配置主数据源
@Primary
@Bean
public DataSource dsOne(DsOneProperties dsOneProperties) throws SQLException {
//配置XA协议数据源,从配置文件中读取相应属性
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(dsOneProperties.getUrl());
mysqlXaDataSource.setPassword(dsOneProperties.getPassword());
mysqlXaDataSource.setUser(dsOneProperties.getUsername());
//将本地事务注册到Atomikos全局事务
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("dsOne");
return xaDataSource;
}
<span class="hljs-meta">@Primary</span>
<span class="hljs-meta">@Bean</span>(name = <span class="hljs-string">"sqlSessionFactory1"</span>)
<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)
<span class="hljs-keyword">throws</span> Exception </span>{
SqlSessionFactoryBean bean = <span class="hljs-keyword">new</span> SqlSessionFactoryBean();
bean.setDataSource(dataSource);
<span class="hljs-keyword">return</span> bean.getObject();
} <span class="hljs-meta">@Primary</span>
<span class="hljs-meta">@Bean</span>(name = <span class="hljs-string">"sqlSessionTemplate1"</span>)
<span class="hljs-function"><span class="hljs-keyword">public</span> SqlSessionTemplate <span class="hljs-title">SqlSessionTemplate1</span><span class="hljs-params">(
@Qualifier(<span class="hljs-string">"sqlSessionFactory1"</span>)</span> SqlSessionFactory sqlSessionFactory) <span class="hljs-keyword">throws</span> Exception </span>{
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> SqlSessionTemplate(sqlSessionFactory);
}

}

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

@Configuration
@MapperScan(basePackages = "com.lwh.mybatistest.mapper2", sqlSessionFactoryRef = "sqlSessionFactory2", sqlSessionTemplateRef = "sqlSessionTemplate2")
public class MyBatisConfigTwo {
@Bean
public DataSource dsTwo(DsTwoProperties dsTwoProperties) throws SQLException {
//配置从数据源
//配置XA协议数据源,从配置文件中读取相应属性
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(dsTwoProperties.getUrl());
mysqlXaDataSource.setPassword(dsTwoProperties.getPassword());
mysqlXaDataSource.setUser(dsTwoProperties.getUsername());
//将本地事务注册到Atomikos全局事务
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("dsTwo");
return xaDataSource;
}
<span class="hljs-meta">@Bean</span>(name = <span class="hljs-string">"sqlSessionFactory2"</span>)
<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)
<span class="hljs-keyword">throws</span> Exception </span>{
SqlSessionFactoryBean bean = <span class="hljs-keyword">new</span> SqlSessionFactoryBean();
bean.setDataSource(dataSource);
<span class="hljs-keyword">return</span> bean.getObject();
} <span class="hljs-meta">@Bean</span>(name = <span class="hljs-string">"sqlSessionTemplate2"</span>)
<span class="hljs-function"><span class="hljs-keyword">public</span> SqlSessionTemplate <span class="hljs-title">SqlSessionTemplate2</span><span class="hljs-params">(
@Qualifier(<span class="hljs-string">"sqlSessionFactory2"</span>)</span> SqlSessionFactory sqlSessionFactory) <span class="hljs-keyword">throws</span> Exception </span>{
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> SqlSessionTemplate(sqlSessionFactory);
}

}

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

@RestController
@RequestMapping("/book")
public class BookController {
@Autowired
BookService bookService;
<span class="hljs-meta">@Autowired</span>
BookService2 bookService2; <span class="hljs-meta">@GetMapping</span>(<span class="hljs-string">"/add1"</span>)
<span class="hljs-meta">@Transactional</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">addBook</span><span class="hljs-params">()</span> </span>{
Book book = <span class="hljs-keyword">new</span> Book();
book.setBookname(<span class="hljs-string">"测试"</span>);
book.setAuthor(<span class="hljs-string">"test:01"</span>);
System.out.println(<span class="hljs-string">"数据库1:&gt;&gt;&gt;&gt;"</span>);
bookService.addBook(book);
System.out.println(<span class="hljs-string">"数据库2:&gt;&gt;&gt;&gt;"</span>);
bookService2.addBook(book);
<span class="hljs-keyword">return</span> <span class="hljs-string">"测试add1操作成功!"</span>;
} <span class="hljs-meta">@GetMapping</span>(<span class="hljs-string">"/add2"</span>)
<span class="hljs-meta">@Transactional</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">addBook2</span><span class="hljs-params">()</span> </span>{
Book book = <span class="hljs-keyword">new</span> Book();
book.setBookname(<span class="hljs-string">"测试add2"</span>);
book.setAuthor(<span class="hljs-string">"test:01"</span>);
System.out.println(<span class="hljs-string">"数据库1:&gt;&gt;&gt;&gt;"</span>);
bookService.addBook(book);
<span class="hljs-keyword">int</span> a = <span class="hljs-number">10</span> / <span class="hljs-number">0</span>;
System.out.println(<span class="hljs-string">"数据库2:&gt;&gt;&gt;&gt;"</span>);
bookService2.addBook(book);
<span class="hljs-keyword">return</span> <span class="hljs-string">"测试add2操作成功!"</span>;
}

}

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

@Service
public class BookService {
<span class="hljs-meta">@Autowired</span>
BookMapper bookMapper; <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>{
bookMapper.insertSelective(book);
}

}

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

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

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

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

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

基于TCC实现的分布式事务

基于SAGA实现的分布式事务

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

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

最新文章

  1. Ubuntu下运行Shell脚本
  2. linux 下shell中if的“-e,-d,-f”是什么意思
  3. 实现鼠标拖动canvas绘制的图片
  4. struts2框架基本操作总结
  5. oracle客户端安装及Plsql devloper连接
  6. POJ1860 Currency Exchange(最短路)
  7. MVC与三层架构的关系
  8. SharePoint 2010以其他用户身份登录的弹出代码
  9. HDU 4642 (13.08.25)
  10. Hadoop中Namenode的HA查询和切换
  11. linux dmesg命令
  12. 移动办公OA系统
  13. Oracle——多表查询
  14. Beta 冲刺(5/7)
  15. 六、Drawable
  16. WebService CXF知识总结
  17. jquery学习总结12-24
  18. JDK动态代理与CGLib动态代理相关问题
  19. LeetCode-Microsoft-Populating Next Right Pointers in Each Node
  20. Spring @Configuration

热门文章

  1. 【AtCoder】CODE FESTIVAL 2016 qual B
  2. Python解Leetcode: 724. Find Pivot Index
  3. Linux系列(14)之工作管理
  4. HDU 3047 带权并查集 入门题
  5. 网络编程之异步IO
  6. 剑指offer21:第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。(注意:这两个序列的长度是相等的)
  7. Reids 连环炮面试(转)
  8. 10 TCP限流技术
  9. Web框架概述——React.js
  10. elment-UI中表头和内容错位