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,直接启动运行即可

posted @ 2021-02-01 16:28  小巫同学  阅读(429)  评论(2编辑  收藏  举报