springbootdruidmybatismysql多数据源事务管理

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实现的分布式事务
之所以有这么多形态,是因为任何事情都没有银弹,只有最合适当前场景的解决方案。

posted @ 2019-10-06 15:37  DiligentCoder  阅读(408)  评论(0编辑  收藏  举报