• springboot2.0 解决多数据源分布式事务问题


    大家都知道,在多数据源的项目中可能会发生的事务问题,因为一个库的事务不可能去操作另一个数据库的事务嘛,这里使用传统的分布式事务解决方案:SpringBoot+JTA+Atomikos 

    使用atomikos对数据库的事务进行统一的管理,但是这套解决方案不适合微服务,需要拿到数据源然后注册到同一个全局事务里面去

    好,那我们如何配置呢?话不多说直接上代码

    pom.xml配置

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     4     <modelVersion>4.0.0</modelVersion>
     5     <parent>
     6         <groupId>org.springframework.boot</groupId>
     7         <artifactId>spring-boot-starter-parent</artifactId>
     8         <version>2.2.10.RELEASE</version>
     9         <relativePath/>
    10     </parent>
    11     <groupId>com.wuwu</groupId>
    12     <artifactId>cboot</artifactId>
    13     <version>0.0.1-SNAPSHOT</version>
    14     <name>cboot</name>
    15     <description>this project for Spring Boot</description>
    16 
    17     <properties>
    18         <java.version>1.8</java.version>
    19     </properties>
    20 
    21     <dependencies>
    22         <dependency>
    23             <groupId>org.springframework.boot</groupId>
    24             <artifactId>spring-boot-starter-data-jdbc</artifactId>
    25         </dependency>
    26         <dependency>
    27             <groupId>org.springframework.boot</groupId>
    28             <artifactId>spring-boot-starter-web</artifactId>
    29         </dependency>
    30 
    31         <!--jta-atomikos 解决多数据源分布式事物问题-->
    32         <dependency>
    33             <groupId>org.springframework.boot</groupId>
    34             <artifactId>spring-boot-starter-jta-atomikos</artifactId>
    35         </dependency>
    36         <!-- mybatis -->
    37         <dependency>
    38             <groupId>org.mybatis.spring.boot</groupId>
    39             <artifactId>mybatis-spring-boot-starter</artifactId>
    40             <version>2.0.0</version>
    41         </dependency>
    42         <!--lombok-->
    43         <dependency>
    44             <groupId>org.projectlombok</groupId>
    45             <artifactId>lombok</artifactId>
    46             <version>1.18.12</version>
    47             <scope>provided</scope>
    48         </dependency>
    49 
    50         <dependency>
    51             <groupId>mysql</groupId>
    52             <artifactId>mysql-connector-java</artifactId>
    53             <scope>runtime</scope>
    54         </dependency>
    55 
    56         <dependency>
    57             <groupId>ch.qos.logback</groupId>
    58             <artifactId>logback-core</artifactId>
    59             <version>1.1.3</version>
    60         </dependency>
    61         <dependency>
    62             <groupId>ch.qos.logback</groupId>
    63             <artifactId>logback-access</artifactId>
    64             <version>1.1.3</version>
    65         </dependency>
    66         <dependency>
    67             <groupId>ch.qos.logback</groupId>
    68             <artifactId>logback-classic</artifactId>
    69             <version>1.1.3</version>
    70         </dependency>
    71 
    72         <dependency>
    73             <groupId>org.springframework.boot</groupId>
    74             <artifactId>spring-boot-starter-test</artifactId>
    75             <scope>test</scope>
    76             <exclusions>
    77                 <exclusion>
    78                     <groupId>org.junit.vintage</groupId>
    79                     <artifactId>junit-vintage-engine</artifactId>
    80                 </exclusion>
    81             </exclusions>
    82         </dependency>
    83     </dependencies>
    84 
    85     <build>
    86         <plugins>
    87             <plugin>
    88                 <groupId>org.springframework.boot</groupId>
    89                 <artifactId>spring-boot-maven-plugin</artifactId>
    90             </plugin>
    91         </plugins>
    92     </build>
    93 
    94 </project>
    View Code

    application.properties配置

     1 server.port=8090
     2 server.servlet.context-path=/
     3 #单数据源
     4 #spring.datasource.driverClassName=com.mysql.jdbc.Driver
     5 #spring.datasource.url=jdbc:mysql://localhost:3306/cboot?useUnicode=true&amp;amp;characterEncoding=UTF-8
     6 #spring.datasource.username=root
     7 #spring.datasource.password=root
     8 
     9 #多数据源-订单
    10 spring.datasource.order.driverClassName=com.mysql.jdbc.Driver
    11 spring.datasource.order.url=jdbc:mysql://localhost:3306/cboot?useUnicode=true&amp;amp;characterEncoding=UTF-8
    12 spring.datasource.order.username=root
    13 spring.datasource.order.password=root
    14 
    15 #多数据源-会员
    16 spring.datasource.member.driverClassName=com.mysql.jdbc.Driver
    17 spring.datasource.member.url=jdbc:mysql://localhost:3306/cboot1?useUnicode=true&amp;amp;characterEncoding=UTF-8
    18 spring.datasource.member.username=root
    19 spring.datasource.member.password=root
    View Code

    MemberConfig以及OrderConfig的配置bean

     1 package com.wuwu.cboot.config;
     2 
     3 import lombok.Data;
     4 import org.springframework.boot.context.properties.ConfigurationProperties;
     5 
     6 /**
     7  * @Description:会员数据库连接属性
     8  * @author: wph
     9  * @Version: 1.0
    10  * @date: 2021/1/15
    11  */
    12 @Data
    13 @ConfigurationProperties(prefix = "spring.datasource.member")
    14 public class MemberConfig {
    15 
    16     private String url;
    17     private String password;
    18     private String username;
    19     private String driverClassName;
    20 }
    View Code
     1 package com.wuwu.cboot.config;
     2 
     3 import lombok.Data;
     4 import org.springframework.boot.context.properties.ConfigurationProperties;
     5 
     6 /**
     7  * @Description:订单数据库连接属性
     8  * @author: wph
     9  * @Version: 1.0
    10  * @date: 2021/1/15
    11  */
    12 @Data
    13 @ConfigurationProperties(prefix = "spring.datasource.order")
    14 public class OrderConfig {
    15 
    16     private String url;
    17     private String password;
    18     private String username;
    19     private String driverClassName;
    20 }
    View Code

    会员数据源MemberDataSourceConfig配置

     1 package com.wuwu.cboot.config;
     2 
     3 import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
     4 import lombok.SneakyThrows;
     5 import org.apache.ibatis.session.SqlSessionFactory;
     6 import org.mybatis.spring.SqlSessionFactoryBean;
     7 import org.mybatis.spring.SqlSessionTemplate;
     8 import org.mybatis.spring.annotation.MapperScan;
     9 import org.springframework.beans.factory.annotation.Qualifier;
    10 import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
    11 import org.springframework.context.annotation.Bean;
    12 import org.springframework.context.annotation.Configuration;
    13 
    14 import javax.sql.DataSource;
    15 
    16 /**
    17  * @Description:会员数据源配置
    18  * @author: wph
    19  * @Version: 1.0
    20  * @date: 2021/1/12
    21  */
    22 @Configuration
    23 @MapperScan(basePackages = "com.wuwu.cboot.member.mapper",sqlSessionTemplateRef = "memberSqlSessionTemplate")
    24 public class MemberDataSourceConfig {
    25 
    26     /**
    27      * @Description: 配置会员数据源
    28      * @author: wph
    29      * @date: 2021/1/15
    30      * @param memberConfig
    31      * @return javax.sql.DataSource
    32      */
    33     @Bean("memberDataSource")
    34     public DataSource memberDataSource(MemberConfig memberConfig){
    35         //1.创建会员的xaDataSource
    36         MysqlXADataSource xaDataSource = new MysqlXADataSource();
    37         xaDataSource.setPassword(memberConfig.getPassword());
    38         xaDataSource.setUser(memberConfig.getUsername());
    39         xaDataSource.setUrl(memberConfig.getUrl());
    40         xaDataSource.setPinGlobalTxToPhysicalConnection(true);
    41 
    42         //2.将会员的xaDataSource注册到全局事务上
    43         AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
    44         dataSource.setXaDataSource(xaDataSource);
    45 
    46         return dataSource;
    47     }
    48 
    49     /**
    50      * @Description: 将会员sqlSessionFactory注入到容器中
    51      * @author: wph
    52      * @date: 2021/1/13
    53      * @param dataSource 
    54      * @return org.apache.ibatis.session.SqlSessionFactory
    55      */
    56     @SneakyThrows(Exception.class)
    57     @Bean("memberSqlSessionFactory")
    58     public SqlSessionFactory memberSqlSessionFactory(@Qualifier("memberDataSource")DataSource dataSource){
    59         SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    60         sqlSessionFactoryBean.setDataSource(dataSource);
    61         return sqlSessionFactoryBean.getObject();
    62     }
    63 
    64     /**
    65      * @Description: 创建SqlSessionTemplate模板
    66      * @author: wph
    67      * @date: 2021/1/18
    68      * @param sqlSessionFactory
    69      * @return org.mybatis.spring.SqlSessionTemplate
    70      */
    71     @Bean("memberSqlSessionTemplate")
    72     public SqlSessionTemplate memberSqlSessionTemplate(@Qualifier("memberSqlSessionFactory") SqlSessionFactory sqlSessionFactory){
    73         return new SqlSessionTemplate(sqlSessionFactory);
    74     }
    75 
    76 }
    View Code

    订单数据源OrderDataSourceConfig配置

     1 package com.wuwu.cboot.config;
     2 
     3 import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
     4 import lombok.SneakyThrows;
     5 import org.apache.ibatis.session.SqlSessionFactory;
     6 import org.mybatis.spring.SqlSessionFactoryBean;
     7 import org.mybatis.spring.SqlSessionTemplate;
     8 import org.mybatis.spring.annotation.MapperScan;
     9 import org.springframework.beans.factory.annotation.Qualifier;
    10 import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
    11 import org.springframework.context.annotation.Bean;
    12 import org.springframework.context.annotation.Configuration;
    13 
    14 import javax.sql.DataSource;
    15 
    16 /**
    17  * @Description:订单数据源配置
    18  * @author: wph
    19  * @Version: 1.0
    20  * @date: 2021/1/15
    21  */
    22 @Configuration
    23 @MapperScan(basePackages = "com.wuwu.cboot.order.mapper",sqlSessionFactoryRef = "orderSqlSessionFactory")
    24 public class OrderDataSourceConfig {
    25 
    26     /**
    27      * @Description: 配置订单数据源
    28      * @author: wph
    29      * @date: 2021/1/15
    30      * @param orderConfig
    31      * @return javax.sql.DataSource
    32      */
    33     @Bean("orderDataSource")
    34     public DataSource orderDataSource(OrderConfig orderConfig){
    35         //1.创建订单xaDataSource
    36         MysqlXADataSource xaDataSource = new MysqlXADataSource();
    37         xaDataSource.setUrl(orderConfig.getUrl());
    38         xaDataSource.setUser(orderConfig.getUsername());
    39         xaDataSource.setPassword(orderConfig.getPassword());
    40         xaDataSource.setPinGlobalTxToPhysicalConnection(true);
    41 
    42         //将xaDataSource注册到全局事务
    43         AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
    44         dataSource.setXaDataSource(xaDataSource);
    45         return dataSource;
    46     }
    47 
    48     /**
    49      * @Description: 配置session工厂
    50      * @author: wph
    51      * @date: 2021/1/18
    52      * @param dataSource
    53      * @return org.apache.ibatis.session.SqlSessionFactory
    54      */
    55     @SneakyThrows
    56     @Bean("orderSqlSessionFactory")
    57     public SqlSessionFactory orderSqlSessionFactory(@Qualifier("orderDataSource") DataSource dataSource){
    58         SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    59         sqlSessionFactoryBean.setDataSource(dataSource);
    60         return sqlSessionFactoryBean.getObject();
    61     }
    62 
    63     /**
    64      * @Description: 创建SqlSessionTemplate模板
    65      * @author: wph
    66      * @date: 2021/1/18
    67      * @param sqlSessionFactory
    68      * @return org.mybatis.spring.SqlSessionTemplate
    69      */
    70     @Bean("orderSqlSessionTemplate")
    71     public SqlSessionTemplate orderSqlSessionTemplate(@Qualifier("orderSqlSessionFactory") SqlSessionFactory sqlSessionFactory){
    72         return new SqlSessionTemplate(sqlSessionFactory);
    73     }
    74 
    75 }
    View Code

    controller层

     1 package com.wuwu.cboot.controller;
     2 
     3 import com.wuwu.cboot.service.MemberService;
     4 import org.springframework.beans.factory.annotation.Autowired;
     5 import org.springframework.web.bind.annotation.RequestMapping;
     6 import org.springframework.web.bind.annotation.RestController;
     7 
     8 /**
     9  * @Description:会员控制层
    10  * @author: wph
    11  * @Version: 1.0
    12  * @date: 2020/10/26
    13  */
    14 @RestController
    15 public class MemberController {
    16 
    17     @Autowired
    18     private MemberService memberService;
    19 
    20     /**
    21      * @Description: 新增会员用户
    22      * @author: wph
    23      * @date: 2021/1/20
    24      * @param age
    25      * @param name
    26      * @return java.lang.Object
    27      */
    28     @RequestMapping("/saveMember")
    29     public Object saveMember(int age,String name){
    30         return memberService.saveMember(age,name);
    31     }
    32 }
    View Code

    service实现

     1 package com.wuwu.cboot.service.impl;
     2 
     3 import com.wuwu.cboot.member.mapper.MemberMapper;
     4 import com.wuwu.cboot.order.mapper.OrderMemberMapper;
     5 import com.wuwu.cboot.service.MemberService;
     6 import org.springframework.stereotype.Service;
     7 import org.springframework.transaction.annotation.Transactional;
     8 
     9 import javax.annotation.Resource;
    10 
    11 /**
    12  * @Description:
    13  * @author: wph
    14  * @Version: 1.0
    15  * @date: 2021/1/12
    16  */
    17 @Service
    18 public class MemberServiceImpl implements MemberService {
    19 
    20     @Resource
    21     private OrderMemberMapper orderMemberMapper;
    22     @Resource
    23     private MemberMapper memberMapper;
    24 
    25     @Transactional
    26     public String saveMember(int age,String name){
    27         //调用会员接口
    28         memberMapper.saveMember(age, name);
    29         //调用订单接口
    30         int i = orderMemberMapper.saveOrder(123, "dawfa");
    31         int num = 13 / 0;
    32         return i == 1 ? "true" : "false";
    33     }
    34 }
    View Code

    mapper层

     1 package com.wuwu.cboot.member.mapper;
     2 
     3 import org.apache.ibatis.annotations.Insert;
     4 import org.apache.ibatis.annotations.Mapper;
     5 
     6 @Mapper
     7 public interface MemberMapper {
     8 
     9     @Insert("insert into member (age,name) values(#{age},#{name})")
    10     int saveMember(int age, String name);
    View Code
     1 package com.wuwu.cboot.order.mapper;
     2 
     3 import org.apache.ibatis.annotations.Insert;
     4 import org.mybatis.spring.annotation.MapperScan;
     5 
     6 @MapperScan
     7 public interface OrderMemberMapper {
     8 
     9     @Insert("insert into order_member (order_id,order_code) values(#{orderId},#{orderCode})")
    10     int saveOrder(int orderId, String orderCode);
    11 }
    View Code

    项目启动类

     1 package com.wuwu.cboot;
     2 
     3 import com.wuwu.cboot.config.MemberConfig;
     4 import com.wuwu.cboot.config.OrderConfig;
     5 import org.springframework.boot.SpringApplication;
     6 import org.springframework.boot.autoconfigure.SpringBootApplication;
     7 import org.springframework.boot.context.properties.EnableConfigurationProperties;
     8 
     9 @SpringBootApplication
    10 @EnableConfigurationProperties({MemberConfig.class, OrderConfig.class})
    11 public class CbootApplication {
    12 
    13     public static void main(String[] args) {
    14         SpringApplication.run(CbootApplication.class, args);
    15     }
    16 
    17 }
    View Code

    最后附上项目结构目录,如图

    ok,直接启动运行即可

  • 相关阅读:
    linux三剑客之sed
    线程与循环的区别?
    Notify和NotifyAll的区别?
    no system images installed for this target这个问题如何解决?
    Intent里ACTION的CALL和DIAL的区别?
    onConfigurationChanged方法的使用
    String和StringBuffer的区别?
    Activity的状态保存
    C#将datatable数据转换成JSON数据的方法
    SQL语句:关于复制表结构和内容到另一张表中的SQL语句
  • 原文地址:https://www.cnblogs.com/w-wu/p/14303119.html
Copyright © 2020-2023  润新知