@Transactional和@DS避免数据源冲突及保证原子性的解决方案(提供gitee源码)

347 阅读8分钟

前言:

在最近的实际工作当中,遇到一个业务场景,需要保证新老数据库的同步,在动态切换数据库当中,遇到了@Transactional和@DS的冲突问题,这里我写了一份个人的总结,从业务还原、原因剖析和如何解决等等一步步阐明。

目录

一、场景模拟

二、原因分析

三、解决方案

(一)更改事务的传播机制

(二)@DSTransactional注解代替@Transactional

四、事务回滚机制

(一)、在Master和Slave事务执行前抛出异常

(二)、当master事务和slave事务中间抛出异常

(三)、在slave方法中抛出异常

(四)、在master和slave事务之后

(五)、临时添加一个temp数据库,进行插入操作,并抛出异常

(六)、嵌套

(七)、使用@DSTransactional注解,在slave和temp之间抛出异常

(八)、使用@DSTransactional注解,在最后抛出异常

五、gitee源码地址

六、总结


一、场景模拟

1、先导入pom.xml依赖

  1. 
     
     


      
      


     
     


     
     


      
      

    
       
       <dependencies>
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      

        
       
       <dependency>
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

            
       
       <groupId>org.springframework.boot
       
       </groupId>
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      

            
       
       <artifactId>spring-boot-starter-jdbc
       
       </artifactId>
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      

        
       
       </dependency>
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      

        
       
       <dependency>
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      

            
       
       <groupId>org.springframework.boot
       
       </groupId>
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      

            
       
       <artifactId>spring-boot-starter-web
       
       </artifactId>
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      

        
       
       </dependency>
      
      


     
     

  10. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  11. 
     
     


      
      


     
     


     
     


      
      

        
       
       <dependency>
      
      


     
     

  12. 
     
     


      
      


     
     


     
     


      
      

            
       
       <groupId>org.springframework.boot
       
       </groupId>
      
      


     
     

  13. 
     
     


      
      


     
     


     
     


      
      

            
       
       <artifactId>spring-boot-starter-test
       
       </artifactId>
      
      


     
     

  14. 
     
     


      
      


     
     


     
     


      
      

            
       
       <scope>test
       
       </scope>
      
      


     
     

  15. 
     
     


      
      


     
     


     
     


      
      

        
       
       </dependency>
      
      


     
     

  16. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  17. 
     
     


      
      


     
     


     
     


      
      

        
       
       <!--数据源切换依赖-->
      
      


     
     

  18. 
     
     


      
      


     
     


     
     


      
      

        
       
       <dependency>
      
      


     
     

  19. 
     
     


      
      


     
     


     
     


      
      

            
       
       <groupId>com.baomidou
       
       </groupId>
      
      


     
     

  20. 
     
     


      
      


     
     


     
     


      
      

            
       
       <artifactId>dynamic-datasource-spring-boot-starter
       
       </artifactId>
      
      


     
     

  21. 
     
     


      
      


     
     


     
     


      
      

            
       
       <version>3.5.2
       
       </version>
      
      


     
     

  22. 
     
     


      
      


     
     


     
     


      
      

        
       
       </dependency>
      
      


     
     

  23. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  24. 
     
     


      
      


     
     


     
     


      
      

        
       
       <!--MySQL驱动依赖-->
      
      


     
     

  25. 
     
     


      
      


     
     


     
     


      
      

        
       
       <dependency>
      
      


     
     

  26. 
     
     


      
      


     
     


     
     


      
      

            
       
       <groupId>mysql
       
       </groupId>
      
      


     
     

  27. 
     
     


      
      


     
     


     
     


      
      

            
       
       <artifactId>mysql-connector-java
       
       </artifactId>
      
      


     
     

  28. 
     
     


      
      


     
     


     
     


      
      

            
       
       <scope>runtime
       
       </scope>
      
      


     
     

  29. 
     
     


      
      


     
     


     
     


      
      

        
       
       </dependency>
      
      


     
     

  30. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  31. 
     
     


      
      


     
     


     
     


      
      

        
       
       <!--Mybatis依赖-->
      
      


     
     

  32. 
     
     


      
      


     
     


     
     


      
      

        
       
       <dependency>
      
      


     
     

  33. 
     
     


      
      


     
     


     
     


      
      

            
       
       <groupId>org.mybatis.spring.boot
       
       </groupId>
      
      


     
     

  34. 
     
     


      
      


     
     


     
     


      
      

            
       
       <artifactId>mybatis-spring-boot-starter
       
       </artifactId>
      
      


     
     

  35. 
     
     


      
      


     
     


     
     


      
      

            
       
       <version>2.2.2
       
       </version>
      
      


     
     

  36. 
     
     


      
      


     
     


     
     


      
      

        
       
       </dependency>
      
      


     
     

  37. 
     
     


      
      


     
     


     
     


      
      

    
       
       </dependencies>
      
      


     
     



   
   

2、yml文件配置了3个数据源,主数据源是master,从数据源是slave,后续临时加了个数据源temp,为了用于事务的测试,数据库均为MySQL。

  1. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      


       
       server:
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      


       
         port: 8080
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
       spring:
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      


       
         datasource:
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      


       
           dynamic:
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      


       
             primary: master
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      


       
             datasource:
      
      


     
     

  10. 
     
     


      
      


     
     


     
     


      
      


       
               master:
      
      


     
     

  11. 
     
     


      
      


     
     


     
     


      
      


       
                 username: root
      
      


     
     

  12. 
     
     


      
      


     
     


     
     


      
      


       
                 password: root
      
      


     
     

  13. 
     
     


      
      


     
     


     
     


      
      


       
                 url: jdbc:mysql://localhost:3306/master?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
      
      


     
     

  14. 
     
     


      
      


     
     


     
     


      
      


       
                 driver-class-name: com.mysql.cj.jdbc.Driver
      
      


     
     

  15. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  16. 
     
     


      
      


     
     


     
     


      
      


       
               slave:
      
      


     
     

  17. 
     
     


      
      


     
     


     
     


      
      


       
                 username: root
      
      


     
     

  18. 
     
     


      
      


     
     


     
     


      
      


       
                 password: root
      
      


     
     

  19. 
     
     


      
      


     
     


     
     


      
      


       
                 url: jdbc:mysql://localhost:3306/slave?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
      
      


     
     

  20. 
     
     


      
      


     
     


     
     


      
      


       
                 driver-class-name: com.mysql.cj.jdbc.Driver
      
      


     
     

  21. 
     
     


      
      


     
     


     
     


      
      


       
               temp:
      
      


     
     

  22. 
     
     


      
      


     
     


     
     


      
      


       
                 username: root
      
      


     
     

  23. 
     
     


      
      


     
     


     
     


      
      


       
                 password: root
      
      


     
     

  24. 
     
     


      
      


     
     


     
     


      
      


       
                 url: jdbc:mysql://localhost:3306/temp?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
      
      


     
     

  25. 
     
     


      
      


     
     


     
     


      
      


       
                 driver-class-name: com.mysql.cj.jdbc.Driver
      
      


     
     

  26. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  27. 
     
     


      
      


     
     


     
     


      
      


       
       mybatis:
      
      


     
     

  28. 
     
     


      
      


     
     


     
     


      
      


       
         mapper-locations: classpath:mapping/*.xml
      
      


     
     



   
   


    
    ![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/ef53b653ad8c42809db942d8527c1030~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5a-C5a-e5peF6KGM:q75.awebp?rk3s=f64ab15b&x-expires=1771230056&x-signature=PALjqdBSxuBS3uk5r8N%2BMHCUyfA%3D)
   
   


   
   

3、分别编写主数据源和从数据源的Mapper层接口

  1. 
     
     


      
      


     
     


     
     


      
      


       
       @Mapper
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      


       
       @DS("master")
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      


       
       public 
       
       interface 
       
       MasterMapper {
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      

    
       
       int 
       
       insertUser
       
       (User user);
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
       }
      
      


     
     



   
   


   
   

  1. 
     
     


      
      


     
     


     
     


      
      


       
       @Mapper
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      


       
       @DS("slave")
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      


       
       public 
       
       interface 
       
       SlaveMapper {
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      

    
       
       int 
       
       insertRole
       
       (Role role);
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
       }
      
      


     
     



   
   

4、分别编写对应的XML文件

  1. 
     
     


      
      


     
     


     
     


      
      


       
       <?xml version="1.0" encoding="UTF-8" ?>
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      


       
       <!DOCTYPE mapper
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

        
       
       PUBLIC 
       
       "-//mybatis.org//DTD Mapper 3.0//EN"
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      

        
       
       "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
       <mapper namespace="com.example.demo.mapper.MasterMapper">
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      

    
       
       <insert id="insertUser" parameterType="com.example.demo.bean.User">
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      


       
               INSERT INTO user (username, password)
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      


       
               VALUES(#{username}, #{password})
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      

    
       
       </insert>
      
      


     
     

  10. 
     
     


      
      


     
     


     
     


      
      


       
       </mapper>
      
      


     
     



   
   


   
   

  1. 
     
     


      
      


     
     


     
     


      
      


       
       <?xml version="1.0" encoding="UTF-8" ?>
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      


       
       <!DOCTYPE mapper
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

        
       
       PUBLIC 
       
       "-//mybatis.org//DTD Mapper 3.0//EN"
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      

        
       
       "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
       <mapper namespace="com.example.demo.mapper.SlaveMapper">
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      

    
       
       <insert id="insertRole" parameterType="com.example.demo.bean.Role">
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      


       
               INSERT INTO role (role)
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      


       
               VALUES(#{role})
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      

    
       
       </insert>
      
      


     
     

  10. 
     
     


      
      


     
     


     
     


      
      


       
       </mapper>
      
      


     
     



   
   

5、编写Service方法

  1. 
     
     


      
      


     
     


     
     


      
      


       
       @Service
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      


       
       public 
       
       class 
       
       UserService {
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Resource
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      

    
       
       private MasterMapper masterMapper;
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Resource
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      

    
       
       private SlaveMapper slaveMapper;
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  10. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Transactional
      
      


     
     

  11. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       Add
       
       (){
      
      


     
     

  12. 
     
     


      
      


     
     


     
     


      
      

        
       
       User 
       
       user 
       
       = 
       
       new 
       
       User();
      
      


     
     

  13. 
     
     


      
      


     
     


     
     


      
      


       
               user.setUsername(
       
       "张三");
      
      


     
     

  14. 
     
     


      
      


     
     


     
     


      
      


       
               user.setPassword(
       
       "123456");
      
      


     
     

  15. 
     
     


      
      


     
     


     
     


      
      


       
               masterMapper.insertUser(user);
      
      


     
     

  16. 
     
     


      
      


     
     


     
     


      
      

        
       
       Role 
       
       role 
       
       = 
       
       new 
       
       Role();
      
      


     
     

  17. 
     
     


      
      


     
     


     
     


      
      


       
               role.setRole(
       
       "管理员");
      
      


     
     

  18. 
     
     


      
      


     
     


     
     


      
      


       
               slaveMapper.insertRole(role);
      
      


     
     

  19. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     

  20. 
     
     


      
      


     
     


     
     


      
      


       
       }
      
      


     
     



   
   


    
    ![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/ef53b653ad8c42809db942d8527c1030~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5a-C5a-e5peF6KGM:q75.awebp?rk3s=f64ab15b&x-expires=1771230056&x-signature=PALjqdBSxuBS3uk5r8N%2BMHCUyfA%3D)
   
   


   
   

6、测试运行,报错如下

Error updating database. Cause: java.sql.SQLSyntaxErrorException: Table

'master.role' doesn't exist

The error may exist in file

[D:\JavaProjects\Java\demo\target\classes\mapping\SlaveMapper.xml]

The error may involve com.example.demo.mapper.SlaveMapper.insertRole-

Inline

The error occurred while setting parameters

SQL: INSERT INTO role (role) VALUES(?)

Cause: java.sql.SQLSyntaxErrorException: Table 'master.role' doesn't

exist
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Table 'master.role' doesn't exist

从报错的表面上来看,是因为在主数据库当中不存在role这个表,可是我们已经切换了数据源呀,可为什么还是报错呢,具体的详细原因,我们往下细说。

二、原因分析

@Transactional开启事务的时候,会先从数据库连接池获取是数据库的连接(基于Spring的AOP切面),我们UserService方法上面没有打上@DS注解,所以Spring默认采用的是主数据源,而且在这之后,这个事务会通过ThreadLocal跟当前线程绑定并也报错了connection连接,通俗的来讲,在进入UserService方法的时候,当前事务以及绑定了数据源Master,在运行到SlaveMapper接口时,因为当前事务的connection连接已经存在,所以拿到的数据源还是默认的Master,于是想找到Slave当中的role表,当然是不可能的,所以只能报错了。

三、解决方案

(一)更改事务的传播机制

其实我们只要更改一下事务的传播机制,将它设置为:Propagation.REQUIRES_NEW即可,意思就是将原有的Spring事务挂起,并创建一个新的事务并分配的一个新的connection,两者不影响,具体操作如下:

1、修改原有的UserService代码

不用通过@DS指定数据源,因为默认是Master;将slave业务操作分离出来,封装到一个Service服务类当中,再通过@Resource注解注入进来,最后还是指定一下回滚策略,遇到异常就回滚。

  1. 
     
     


      
      


     
     


     
     


      
      


       
       @Service
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      


       
       public 
       
       class 
       
       UserService {
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Resource
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      

    
       
       private MasterMapper masterMapper;
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Resource
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      

    
       
       private SlaveService slaveService;
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  10. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Transactional(rollbackFor = Exception.class)
      
      


     
     

  11. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       Add
       
       (){
      
      


     
     

  12. 
     
     


      
      


     
     


     
     


      
      

        
       
       User 
       
       user 
       
       = 
       
       new 
       
       User();
      
      


     
     

  13. 
     
     


      
      


     
     


     
     


      
      


       
               user.setUsername(
       
       "张三");
      
      


     
     

  14. 
     
     


      
      


     
     


     
     


      
      


       
               user.setPassword(
       
       "123456");
      
      


     
     

  15. 
     
     


      
      


     
     


     
     


      
      


       
               masterMapper.insertUser(user);
      
      


     
     

  16. 
     
     


      
      


     
     


     
     


      
      


       
               slaveService.slave();
      
      


     
     

  17. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     

  18. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  19. 
     
     


      
      


     
     


     
     


      
      


       
       }
      
      


     
     



   
   

2、 编写SlaveService服务代码

必须通过@DS指定一下数据源为slave,在slave方法上面重新修改一下事务的传播机制即可

  1. 
     
     


      
      


     
     


     
     


      
      


       
       @Service
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      


       
       @DS("slave")
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      


       
       public 
       
       class 
       
       SlaveService {
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Resource
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      

    
       
       private SlaveMapper slaveMapper;
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       slave
       
       (){
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      

        
       
       Role 
       
       role 
       
       = 
       
       new 
       
       Role();
      
      


     
     

  10. 
     
     


      
      


     
     


     
     


      
      


       
               role.setRole(
       
       "管理员");
      
      


     
     

  11. 
     
     


      
      


     
     


     
     


      
      


       
               slaveMapper.insertRole(role);
      
      


     
     

  12. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     

  13. 
     
     


      
      


     
     


     
     


      
      


       
       }
      
      


     
     



   
   

3、其他的保持不变,最后我们再测试一下,看一下输出结果,成功了!

(二)@DSTransactional注解代替@Transactional

我们可以使用@DSTransactional注解代替@Transactional即可,其他什么都不用动,也是最简单的方法。

1、导入pom.xml依赖

  1. 
     
     


      
      


     
     


     
     


      
      

    
       
       <dependency>
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      

      
       
       <groupId>com.baomidou
       
       </groupId>
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

      
       
       <artifactId>dynamic-datasource-spring-boot-starter
       
       </artifactId>
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      

      
       
       <version>3.5.0
       
       </version>
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      

    
       
       </dependency>
      
      


     
     



   
   

2、修改UserService代码

  1. 
     
     


      
      


     
     


     
     


      
      


       
       @Service
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      


       
       public 
       
       class 
       
       UserService {
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Resource
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      

    
       
       private MasterMapper masterMapper;
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Resource
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      

    
       
       private SlaveMapper slaveMapper;
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      

 
      
      


     
     

  10. 
     
     


      
      


     
     


     
     


      
      

    
       
       @DSTransactional
      
      


     
     

  11. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       Add
       
       (){
      
      


     
     

  12. 
     
     


      
      


     
     


     
     


      
      

        
       
       User 
       
       user 
       
       = 
       
       new 
       
       User();
      
      


     
     

  13. 
     
     


      
      


     
     


     
     


      
      


       
               user.setUsername(
       
       "张三");
      
      


     
     

  14. 
     
     


      
      


     
     


     
     


      
      


       
               user.setPassword(
       
       "123456");
      
      


     
     

  15. 
     
     


      
      


     
     


     
     


      
      


       
               masterMapper.insertUser(user);
      
      


     
     

  16. 
     
     


      
      


     
     


     
     


      
      

        
       
       Role 
       
       role 
       
       = 
       
       new 
       
       Role();
      
      


     
     

  17. 
     
     


      
      


     
     


     
     


      
      


       
               role.setRole(
       
       "管理员");
      
      


     
     

  18. 
     
     


      
      


     
     


     
     


      
      


       
               slaveMapper.insertRole(role);
      
      


     
     

  19. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     

  20. 
     
     


      
      


     
     


     
     


      
      


       
       }
      
      


     
     



   
   

3、运行测试,查看输出结果,成功!

四、事务回滚机制

(一)、在Master和Slave事务执行前抛出异常

UserService类:

  1. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Transactional(rollbackFor = Exception.class)
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       Add
       
       (){
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

        
       
       User 
       
       user 
       
       = 
       
       new 
       
       User();
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      


       
               user.setUsername(
       
       "张三");
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
               user.setPassword(
       
       "123456");
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      

        
       
       int 
       
       a 
       
       = 
       
       1/
       
       0;
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      


       
               masterMapper.insertUser(user);
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      


       
               slaveService.slave();
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     



   
   

结果:数据保持一致

(二)、当master事务和slave事务中间抛出异常

UserService类:

  1. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Transactional(rollbackFor = Exception.class)
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       Add
       
       (){
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

        
       
       User 
       
       user 
       
       = 
       
       new 
       
       User();
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      


       
               user.setUsername(
       
       "张三");
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
               user.setPassword(
       
       "123456");
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      


       
               masterMapper.insertUser(user);
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      

        
       
       int 
       
       a 
       
       = 
       
       1/
       
       0;
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      


       
               slaveService.slave();
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     



   
   

结果:回滚master事务,slave事务无影响

(三)、在slave方法中抛出异常

SlaveService类:

  1. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       slave
       
       (){
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

        
       
       Role 
       
       role 
       
       = 
       
       new 
       
       Role();
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      


       
               role.setRole(
       
       "管理员");
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
               slaveMapper.insertRole(role);
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      

        
       
       int 
       
       a 
       
       = 
       
       1/
       
       0;
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     



   
   

结果:master和slave事务都会进行回滚

(四)、在master和slave事务之后

UserService类:

  1. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Transactional(rollbackFor = Exception.class)
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       Add
       
       (){
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

        
       
       User 
       
       user 
       
       = 
       
       new 
       
       User();
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      


       
               user.setUsername(
       
       "张三");
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
               user.setPassword(
       
       "123456");
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      


       
               masterMapper.insertUser(user);
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      


       
               slaveService.slave();
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      

        
       
       int 
       
       a 
       
       = 
       
       1/
       
       0;
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     



   
   

结果:master事务回滚,slave已经提交事务,入库

(五)、临时添加一个temp数据库,进行插入操作,并抛出异常

UserService类:

  1. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Transactional(rollbackFor = Exception.class)
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       Add
       
       (){
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

        
       
       User 
       
       user 
       
       = 
       
       new 
       
       User();
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      


       
               user.setUsername(
       
       "张三");
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
               user.setPassword(
       
       "123456");
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      


       
               masterMapper.insertUser(user);
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      


       
               slaveService.slave();
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      


       
               tempService.temp();
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     



   
   

TempService类:

  1. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       temp
       
       (){
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

        
       
       Car 
       
       car 
       
       = 
       
       new 
       
       Car();
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      


       
               car.setCar(
       
       "AE86");
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
               tempMapper.insertCar(car);
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      

        
       
       int 
       
       a 
       
       = 
       
       1/
       
       0;
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     



   
   

结果:master回滚,slave事务提交,temp回滚

(六)、嵌套

UserService类:

  1. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Transactional(rollbackFor = Exception.class)
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       Add
       
       (){
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

        
       
       User 
       
       user 
       
       = 
       
       new 
       
       User();
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      


       
               user.setUsername(
       
       "张三");
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
               user.setPassword(
       
       "123456");
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      


       
               masterMapper.insertUser(user);
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      


       
               slaveService.slave();
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     



   
   

SlaveService类:

  1. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       slave
       
       (){
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

        
       
       Role 
       
       role 
       
       = 
       
       new 
       
       Role();
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      


       
               role.setRole(
       
       "管理员");
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
               slaveMapper.insertRole(role);
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      


       
               tempService.temp();
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     



   
   

TempService类:

  1. 
     
     


      
      


     
     


     
     


      
      

    
       
       @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       temp
       
       (){
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

        
       
       Car 
       
       car 
       
       = 
       
       new 
       
       Car();
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      


       
               car.setCar(
       
       "AE86");
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
               tempMapper.insertCar(car);
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      

        
       
       int 
       
       a 
       
       = 
       
       1/
       
       0;
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     



   
   

结果:master回滚,slave回滚,temp回滚

(七)、使用@DSTransactional注解,在slave和temp之间抛出异常

UserService类

  1. 
     
     


      
      


     
     


     
     


      
      

    
       
       @DSTransactional
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       Add
       
       (){
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

        
       
       User 
       
       user 
       
       = 
       
       new 
       
       User();
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      


       
               user.setUsername(
       
       "张三");
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
               user.setPassword(
       
       "123456");
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      


       
               masterMapper.insertUser(user);
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      

        
       
       Role 
       
       role 
       
       = 
       
       new 
       
       Role();
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      


       
               role.setRole(
       
       "管理员");
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      


       
               slaveMapper.insertRole(role);
      
      


     
     

  10. 
     
     


      
      


     
     


     
     


      
      

        
       
       int a=
       
       1/
       
       0;
      
      


     
     

  11. 
     
     


      
      


     
     


     
     


      
      

        
       
       Car 
       
       car 
       
       = 
       
       new 
       
       Car();
      
      


     
     

  12. 
     
     


      
      


     
     


     
     


      
      


       
               car.setCar(
       
       "AE86");
      
      


     
     

  13. 
     
     


      
      


     
     


     
     


      
      


       
               tempMapper.insertCar(car);
      
      


     
     

  14. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     



   
   

结果:master回滚,slave回滚、temp回滚

(八)、使用@DSTransactional注解,在最后抛出异常

  1. 
     
     


      
      


     
     


     
     


      
      

    
       
       @DSTransactional
      
      


     
     

  2. 
     
     


      
      


     
     


     
     


      
      

    
       
       public 
       
       void 
       
       Add
       
       (){
      
      


     
     

  3. 
     
     


      
      


     
     


     
     


      
      

        
       
       User 
       
       user 
       
       = 
       
       new 
       
       User();
      
      


     
     

  4. 
     
     


      
      


     
     


     
     


      
      


       
               user.setUsername(
       
       "张三");
      
      


     
     

  5. 
     
     


      
      


     
     


     
     


      
      


       
               user.setPassword(
       
       "123456");
      
      


     
     

  6. 
     
     


      
      


     
     


     
     


      
      


       
               masterMapper.insertUser(user);
      
      


     
     

  7. 
     
     


      
      


     
     


     
     


      
      

        
       
       Role 
       
       role 
       
       = 
       
       new 
       
       Role();
      
      


     
     

  8. 
     
     


      
      


     
     


     
     


      
      


       
               role.setRole(
       
       "管理员");
      
      


     
     

  9. 
     
     


      
      


     
     


     
     


      
      


       
               slaveMapper.insertRole(role);
      
      


     
     

  10. 
     
     


      
      


     
     


     
     


      
      

        
       
       Car 
       
       car 
       
       = 
       
       new 
       
       Car();
      
      


     
     

  11. 
     
     


      
      


     
     


     
     


      
      


       
               car.setCar(
       
       "AE86");
      
      


     
     

  12. 
     
     


      
      


     
     


     
     


      
      


       
               tempMapper.insertCar(car);
      
      


     
     

  13. 
     
     


      
      


     
     


     
     


      
      

        
       
       int a=
       
       1/
       
       0;
      
      


     
     

  14. 
     
     


      
      


     
     


     
     


      
      


       
           }
      
      


     
     



   
   

结果:同上

五、gitee源码地址

gitee.com/huang-tuant…

六、总结

以上就是我目前对于这种问题的分析与解决方案,参考了网上各种各样的解决方案,写了一份总结文档。

版权声明:本文为CSDN博主「黄团团」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接

通过此篇文章了解到,普通的数据源切换中,事务失效的原因;
多数据源中,可在事务中开启新事物,但是由于事务的传播性,导致外部事物无法影响内部事务,但是内部事务会直接影响外部事务;
可以依然怒多数据源的事务来解决类似问题,既: @DSTransactional,这样就达成了既定的目标,事务一致性;与单数据源事务,基本没有区别;

本文转自 jimolvxing.blog.csdn.net/article/det…,如有侵权,请联系删除。