【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

06-02 1437阅读

详解Transactional

  • 0.介绍
  • 1.rollbackFor
    • 1.1 介绍
    • 1.2 演示
      • 1.2.1 抛IO异常--提交
      • 1.2.2 rollbackFor 指定所有的异常回滚
      • 1.3 结论
      • 2.事务隔离级别
        • 2.1 MySQL 事务隔离级别(回顾)
        • 2.2 Spring事务隔离级别
        • 2.3 ioslation 属性
        • 3.Spring事务传播机制
          • 3.1 什么是事务传播机制?
          • 3.2 事务的传播机制有哪些
          • 3.3 Spring 事务传播机制使用
            • 3.3.1 Propagation.REQUIRED(加入事务)
            • 3.3.2 Propagation.REQUIRES_NEW(新建事务)
            • 3.3.3 Propagation.NEVER (不⽀持当前事务, 抛异常)
            • 3.3.4 Propagation.NESTED(嵌套事务)
            • 3.4 REQUIRED和NESTED的区别
              • 3.4.1 NESTED--某一嵌套事务回滚
              • 3.4.2 REQUIRED--部分事务回滚
              • 3.4.3 总结
              • 4. 总结

                0.介绍

                上篇文章讲解@Transactional的使用,这篇主要讲解 @Transactional 注解当中的三个常⻅属性及使用:

                1. rollbackFor: 异常回滚属性. 指定能够触发事务回滚的异常类型. 可以指定多个异常类型
                2. Isolation: 事务的隔离级别. 默认值为 Isolation.DEFAULT
                3. propagation: 事务的传播机制. 默认值为 Propagation.REQUIRED

                注:使用的是该文章中的表格和基本代码

                1.rollbackFor

                1.1 介绍

                @Transactional 默认只在遇到运⾏时异常(RuntimeException)和Error时才会回滚, 其他情况下不回滚. 即在Exception的⼦类中, 除了RuntimeException及其⼦类,其他的都不回滚。

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                1.2 演示

                1.2.1 抛IO异常–提交

                Controller接口:

                @RequestMapping("/user")
                @RestController
                public class UserInfoController {
                    @Autowired
                    private UserInfoService userInfoService;
                    @RequestMapping("/update")
                    public boolean  updateUserInfo(@RequestBody LogInfo logInfo) throws IOException {
                        boolean flag =  userInfoService.updateUserInfo(logInfo);
                        return flag;
                    }
                }
                

                Service接口:

                @Slf4j
                @Service
                public class UserInfoService {
                    @Autowired
                    private LogInfoMapper logInfoMapper;
                    @Autowired
                    private UserInfoMapper userInfoMapper;
                    @Transactional
                    public boolean updateUserInfo(LogInfo logInfo) throws IOException {
                        // 处理 from 向 to 转账 10
                        // 事务逻辑
                        // 1.from 的账户 -10
                        userInfoMapper.updateDelete(logInfo.getFrom(),logInfo.getNum());
                        // 2.to 的账户 +10
                        userInfoMapper.updateAdd(logInfo.getTo(),logInfo.getNum());
                        // 3. 记录日志
                        logInfoMapper.insert(logInfo);
                        // 故意设置抛异常
                        if(true){
                            throw new IOException();
                        }
                        return true;
                    }
                }
                

                使用postman测试:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                运行结果:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                前端接收到的数据报错,但是后端已经把事务提交了。

                MySQL中数据(原始数据 zangsan–70;lisi–130):

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                可以看到,数据库中的数据发生了改变

                1.2.2 rollbackFor 指定所有的异常回滚

                如果我们需要所有异常都回滚, 需要来配置 @Transactional 注解当中的 rollbackFor 属性, 通过 rollbackFor 这个属性指定出现何种异常类型时事务进⾏回滚

                Service接口代码:

                @Slf4j
                @Service
                public class UserInfoService {
                    @Autowired
                    private LogInfoMapper logInfoMapper;
                    @Autowired
                    private UserInfoMapper userInfoMapper;
                    //rollbackFor = Exception.class 指定所有异常回滚
                    @Transactional(rollbackFor = Exception.class)
                    public boolean updateUserInfo(LogInfo logInfo) throws IOException {
                        // 处理 from 向 to 转账 10
                        // 事务逻辑
                        // 1.from 的账户 -10
                        userInfoMapper.updateDelete(logInfo.getFrom(),logInfo.getNum());
                        // 2.to 的账户 +10
                        userInfoMapper.updateAdd(logInfo.getTo(),logInfo.getNum());
                        // 3. 记录日志
                        logInfoMapper.insert(logInfo);
                        // 故意设置抛异常
                        if(true){
                            throw new IOException();
                        }
                        return true;
                    }
                }
                

                使用postman测试:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                运行结果:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                通过运行结果可以发现,只是把会话释放了,没有提交事务

                MySQL:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                MySQL中的数据没有发生改变

                1.3 结论

                结论:

                • 在Spring的事务管理中,默认只在遇到运⾏时异常RuntimeException和Error时才会回滚.

                • 如果需要回滚指定类型的异常, 可以通过rollbackFor属性来指定.

                2.事务隔离级别

                2.1 MySQL 事务隔离级别(回顾)

                SQL 标准定义了四种隔离级别, MySQL 全都⽀持. 这四种隔离级别分别是:

                1. 读未提交(READ UNCOMMITTED): 读未提交, 也叫未提交读. 该隔离级别的事务可以看到其他事务中未提交的数据.

                因为其他事务未提交的数据可能会发⽣回滚, 但是该隔离级别却可以读到, 我们把该级别读到的数据称之为脏数据, 这个问题称之为脏读.

                比特就业课

                1. 读提交(READ COMMITTED): 读已提交, 也叫提交读. 该隔离级别的事务能读取到已经提交事务的数据.

                该隔离级别不会有脏读的问题.但由于在事务的执⾏中可以读取到其他事务提交的结果, 所以在不同时间的相同 SQL 查询可能会得到不同的结果, 这种现象叫做不可重复读

                1. 可重复读(REPEATABLE READ): 事务不会读到其他事务对已有数据的修改, 即使其他事务已提交. 也就可以确保同⼀事务多次查询的结果⼀致, 但是其他事务新插⼊的数据, 是可以感知到的. 这也就引发了幻读问题. 可重复读, 是 MySQL 的默认事务隔离级别.

                ⽐如此级别的事务正在执⾏时, 另⼀个事务成功的插⼊了某条数据, 但因为它每次查询的结果都是⼀样的, 所以会导致查询不到这条数据, ⾃⼰重复插⼊时⼜失败(因为唯⼀约束的原因). 明明在事务中查询不到这条信息,但⾃⼰就是插⼊不进去, 这个现象叫幻读.

                1. 串⾏化(SERIALIZABLE): 序列化, 事务最⾼隔离级别. 它会强制事务排序, 使之不会发⽣冲突, 从⽽解决了脏读, 不可重复读和幻读问题, 但因为执⾏效率低, 所以真正使⽤的场景并不多
                事务隔离级别脏读不可重复读幻读
                读未提交 (READ UNCOMMITTED)
                读已提交 (READ COMMITTED)×
                可重复读 (REPEATABLE READ)××
                串行化 (SERIALIZABLE)×××

                2.2 Spring事务隔离级别

                Spring 中事务隔离级别有5 种:

                1. Isolation.DEFAULT : 以连接的数据库的事务隔离级别为主.
                2. Isolation.READ_UNCOMMITTED : 读未提交, 对应SQL标准中 READ UNCOMMITTED
                3. Isolation.READ_COMMITTED : 读已提交,对应SQL标准中 READ COMMITTED
                4. Isolation.REPEATABLE_READ : 可重复读, 对应SQL标准中 REPEATABLE READ
                5. Isolation.SERIALIZABLE : 串⾏化, 对应SQL标准中 SERIALIZABLE

                2.3 ioslation 属性

                Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进⾏设置

                赋值时需要使用Isolation枚举类:

                public enum Isolation {
                    DEFAULT(-1),
                    READ_UNCOMMITTED(1),
                    READ_COMMITTED(2),
                    REPEATABLE_READ(4),
                    SERIALIZABLE(8);
                    private final int value;
                    private Isolation(int value) {
                        this.value = value;
                    }
                    public int value() {
                        return this.value;
                    }
                }
                

                举例:

                @Slf4j
                @Service
                public class UserInfoService {
                    @Autowired
                    private LogInfoMapper logInfoMapper;
                    @Autowired
                    private UserInfoMapper userInfoMapper;
                    // Isolation.READ_COMMITTED 设置读已提交
                    @Transactional(isolation = Isolation.READ_COMMITTED)
                    public boolean updateUserInfo(LogInfo logInfo) throws IOException {
                        // 处理 from 向 to 转账 10
                        // 事务逻辑
                        // 1.from 的账户 -10
                        userInfoMapper.updateDelete(logInfo.getFrom(),logInfo.getNum());
                        // 2.to 的账户 +10
                        userInfoMapper.updateAdd(logInfo.getTo(),logInfo.getNum());
                        // 3. 记录日志
                        logInfoMapper.insert(logInfo);
                        // 故意设置抛异常
                        if(true){
                            throw new IOException();
                        }
                        return true;
                    }
                }
                

                3.Spring事务传播机制

                3.1 什么是事务传播机制?

                事务传播机制就是: 多个事务⽅法存在调⽤关系时, 事务是如何在这些⽅法间进⾏传播的.

                ⽐如有两个⽅法A, B都被 @Transactional 修饰, A⽅法调⽤B⽅法

                A⽅法运⾏时, 会开启⼀个事务. 当A调⽤B时, B⽅法本⾝也是事务, 此时B⽅法运⾏时, 是加⼊A的事务, 还是创建⼀个新的事务呢?

                这个就涉及到了事务的传播机制.

                ⽐如公司流程管理

                执⾏任务之前, 需要先写执⾏⽂档, 任务执⾏结束, 再写总结汇报,此时A部⻔有⼀项⼯作, 需要B部⻔的⽀援, 此时B部⻔是直接使⽤A部⻔的⽂档, 还是新建⼀个⽂档呢?

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                事务隔离级别解决的是多个事务同时调⽤⼀个数据库的问题:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                ⽽事务传播机制解决的是⼀个事务在多个节点(⽅法)中传递的问题:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                3.2 事务的传播机制有哪些

                @Transactional 注解⽀持事务传播机制的设置, 通过 propagation 属性来指定传播⾏为

                Spring 事务传播机制有以下 7 种:

                1.Propagation.REQUIRED: 默认的事务传播级别. 如果当前存在事务, 则加⼊该事务. 如果当前没有事务, 则创建⼀个新的事务.

                2. Propagation.SUPPORTS : 如果当前存在事务, 则加⼊该事务. 如果当前没有事务, 则以非事务的⽅式继续运行.

                3. Propagation.MANDATORY :强制性. 如果当前存在事务, 则加⼊该事务. 如果当前没有事务, 则抛出异常.

                4. Propagation.REQUIRES_NEW : 创建⼀个新的事务. 如果当前存在事务, 则把当前事务挂起. 也就是说不管外部⽅法是否开启事务, Propagation.REQUIRES_NEW 修饰的内部⽅法都会新开

                启⾃⼰的事务, 且开启的事务相互独⽴, 互不⼲扰.

                5. Propagation.NOT_SUPPORTED : 以⾮事务⽅式运⾏, 如果当前存在事务, 则把当前事务挂起(不⽤).

                6. Propagation.NEVER : 以⾮事务⽅式运⾏, 如果当前存在事务, 则抛出异常.

                6. Propagation.NESTED : 如果当前存在事务, 则创建⼀个事务作为当前事务的嵌套事务来运⾏.如果当前没有事务, 则该取值等价于 PROPAGATION_REQUIRED

                枚举类Propagation:

                public enum Propagation {
                    REQUIRED(0),
                    SUPPORTS(1),
                    MANDATORY(2),
                    REQUIRES_NEW(3),
                    NOT_SUPPORTED(4),
                    NEVER(5),
                    NESTED(6);
                    private final int value;
                    private Propagation(int value) {
                        this.value = value;
                    }
                    public int value() {
                        return this.value;
                    }
                }
                

                3.3 Spring 事务传播机制使用

                对于以上事务传播机制,我们重点关注以下两个就可以了:

                1. REQUIRED(默认值)
                2. REQUIRES_NEW

                3.3.1 Propagation.REQUIRED(加入事务)

                程序设计:

                在controller中依次调用两个service,传播机制都设置为Propagation.REQUIRED。

                (1)Controller层:

                @RequestMapping("/user")
                @RestController
                public class UserInfoController {
                    @Autowired
                    private UserInfoService userInfoService;
                    @Autowired
                    private LogInfoService logInfoService;
                    @RequestMapping("/update1")
                    @Transactional(propagation = Propagation.REQUIRED)
                    public boolean  updateUserInfo(@RequestBody LogInfo logInfo) {
                        // 1. 更新用户信息
                        boolean flag1 =  userInfoService.updateUserInfo(logInfo);
                        // 2.插入日志
                        boolean flag2 = logInfoService.insert(logInfo);
                        if(flag1 && flag2){
                            return true;
                        }
                        return false;
                    }
                }
                

                (1)Service:

                @Slf4j
                @Service
                public class UserInfoService {
                    @Autowired
                    private LogInfoMapper logInfoMapper;
                    @Autowired
                    private UserInfoMapper userInfoMapper;
                    @Transactional(propagation = Propagation.MANDATORY)
                    public boolean updateUserInfo(LogInfo logInfo) {
                        // 处理 from 向 to 转账 10
                        // 事务逻辑
                        // 1.from 的账户 -10
                        userInfoMapper.updateDelete(logInfo.getFrom(),logInfo.getNum());
                        // 2.to 的账户 +10
                        userInfoMapper.updateAdd(logInfo.getTo(),logInfo.getNum());
                        // 3. 记录日志
                        // 在LogInfoService实现
                        
                        return true;
                    }
                }
                

                (2)Service(故意在该方法中设置一个错误):

                @Service
                public class LogInfoService {
                    @Autowired
                    private LogInfoMapper logInfoMapper;
                    @Transactional(propagation = Propagation.REQUIRED)
                    public boolean insert(LogInfo logInfo){
                		//错误点
                        int n = 10/0;
                        //记录日志
                        int flag = logInfoMapper.insert(logInfo);
                        if(flag>0 && n>=0){
                            return true;
                        }
                        return  false;
                    }
                }
                

                使用postman测试:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                运行结果:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                在运行结果中可以看到,会话只是释放了没有提交

                MySQL中的数据(原来的数据:zangsan–55;lisi–145):

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                可以看到数据没有发生改变

                事务的流程:

                运⾏程序, 发现数据库没有插⼊任何数据.

                流程描述:

                (1)Controller.updateUserInfo方法开始事务

                (2) userInfoService.updateUserInfo(logInfo)(执⾏成功) (和Controller.updateUserInfo 使⽤同⼀个事务)

                (3)记录操作⽇志, 插⼊⼀条数据(出现异常, 执⾏失败) (和Controller.updateUserInfo 使⽤同⼀个事务)

                (5)因为步骤3出现异常, 事务回滚;步骤2和3使⽤同⼀个事务, 所以步骤2的数据也回滚了.

                3.3.2 Propagation.REQUIRES_NEW(新建事务)

                程序设计:

                在controller中依次调用两个service,传播机制都设置为Propagation.REQUIRES_NEW。

                (1)ontroller层:

                @RequestMapping("/user")
                @RestController
                public class UserInfoController {
                    @Autowired
                    private UserInfoService userInfoService;
                    @Autowired
                    private LogInfoService logInfoService;
                    @RequestMapping("/update1")
                    @Transactional(propagation = Propagation.REQUIRES_NEW)
                    public boolean  updateUserInfo(@RequestBody LogInfo logInfo) {
                        // 1. 更新用户信息
                        boolean flag1 =  userInfoService.updateUserInfo(logInfo);
                        // 2.插入日志
                        boolean flag2 = logInfoService.insert(logInfo);
                        if(flag1 && flag2){
                            return true;
                        }
                        return false;
                    }
                }
                

                (1)Service:

                @Slf4j
                @Service
                public class UserInfoService {
                    @Autowired
                    private LogInfoMapper logInfoMapper;
                    @Autowired
                    private UserInfoMapper userInfoMapper;
                    @Transactional(propagation = Propagation.REQUIRES_NEW)
                    public boolean updateUserInfo(LogInfo logInfo) {
                        // 处理 from 向 to 转账 10
                        // 事务逻辑
                        // 1.from 的账户 -10
                        userInfoMapper.updateDelete(logInfo.getFrom(),logInfo.getNum());
                        // 2.to 的账户 +10
                        userInfoMapper.updateAdd(logInfo.getTo(),logInfo.getNum());
                        // 3. 记录日志
                        // 在LogInfoService实现
                        
                        return true;
                    }
                }
                

                (2)Service(故意在该方法中设置一个错误):

                @Service
                public class LogInfoService {
                    @Autowired
                    private LogInfoMapper logInfoMapper;
                    @Transactional(propagation = Propagation.REQUIRES_NEW)
                    public boolean insert(LogInfo logInfo){
                		//错误点
                        int n = 10/0;
                        //记录日志
                        int flag = logInfoMapper.insert(logInfo);
                        if(flag>0 && n>=0){
                            return true;
                        }
                        return  false;
                    }
                }
                

                使用postman测试:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                运行结果:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                在运行结果中可以看到,有一个会话提交了

                MySQL中的数据(原来的数据:zangsan–55;lisi–145):

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                可以看到user_info中两个用户的数据发生改变,但是log_info中的数据没有发生改变。

                流程描述:

                (1)Controller.updateUserInfo方法开始事务

                (2) userInfoService.updateUserInfo(logInfo)(开新的事物并执⾏成功) (和Controller.updateUserInfo 使用的不是同⼀个事务)

                (3)记录操作⽇志, 插⼊⼀条数据(开新的事物,出现异常, 执⾏失败,该事物回滚)

                (5)步骤3出现异常, 事务回滚,但是步骤2和3不使用同⼀个事务, 两个事物相互独立,步骤二的事物提交。

                3.3.3 Propagation.NEVER (不⽀持当前事务, 抛异常)

                在UserInfoService中的方法中把传播机制改为 Propagation.NEVER,其他的传播机制都设置为Propagation.REQUIRES_NEW。

                UserInfoService:

                @Slf4j
                @Service
                public class UserInfoService {
                    @Autowired
                    private LogInfoMapper logInfoMapper;
                    @Autowired
                    private UserInfoMapper userInfoMapper;
                //    @Transactional(propagation = Propagation.REQUIRES_NEW)
                    @Transactional(propagation = Propagation.NEVER)
                    public boolean updateUserInfo(LogInfo logInfo) {
                        // 处理 from 向 to 转账 10
                        // 事务逻辑
                        // 1.from 的账户 -10
                        userInfoMapper.updateDelete(logInfo.getFrom(),logInfo.getNum());
                        // 2.to 的账户 +10
                        userInfoMapper.updateAdd(logInfo.getTo(),logInfo.getNum());
                        // 3. 记录日志
                        // 在LogInfoService实现
                        return true;
                    }
                }
                

                LogInfoService:

                @Service
                public class LogInfoService {
                    @Autowired
                    private LogInfoMapper logInfoMapper;
                    @Transactional(propagation = Propagation.REQUIRES_NEW)
                    public boolean insert(LogInfo logInfo) {
                        //记录日志
                        int flag = logInfoMapper.insert(logInfo);
                        if (flag > 0) {
                            return true;
                        }
                        return false;
                    }
                }
                

                使用postman测试:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                运行结果:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                执行程序报错,没有数据插入

                3.3.4 Propagation.NESTED(嵌套事务)

                将上述UserService 和LogService 中相关⽅法事务传播机制改为 Propagation.NESTED

                (1)Controller层:

                @RequestMapping("/user")
                @RestController
                public class UserInfoController {
                    @Autowired
                    private UserInfoService userInfoService;
                    @Autowired
                    private LogInfoService logInfoService;
                    @RequestMapping("/update1")
                    @Transactional(propagation = Propagation.REQUIRED)
                    public boolean  updateUserInfo(@RequestBody LogInfo logInfo) {
                        // 1. 更新用户信息
                        boolean flag1 =  userInfoService.updateUserInfo(logInfo);
                        // 2.插入日志
                        boolean flag2 = logInfoService.insert(logInfo);
                        if(flag1 && flag2){
                            return true;
                        }
                        return false;
                    }
                }
                

                (1)Service:

                @Slf4j
                @Service
                public class UserInfoService {
                    @Autowired
                    private LogInfoMapper logInfoMapper;
                    @Autowired
                    private UserInfoMapper userInfoMapper;
                    @Transactional(propagation = Propagation.NESTED)
                    public boolean updateUserInfo(LogInfo logInfo) {
                        // 处理 from 向 to 转账 10
                        // 事务逻辑
                        // 1.from 的账户 -10
                        userInfoMapper.updateDelete(logInfo.getFrom(),logInfo.getNum());
                        // 2.to 的账户 +10
                        userInfoMapper.updateAdd(logInfo.getTo(),logInfo.getNum());
                        // 3. 记录日志
                        // 在LogInfoService实现
                        
                        return true;
                    }
                }
                

                (2)Service(故意在该方法中设置一个错误):

                @Service
                public class LogInfoService {
                    @Autowired
                    private LogInfoMapper logInfoMapper;
                    @Transactional(propagation = Propagation.NESTED)
                    public boolean insert(LogInfo logInfo){
                		//错误点
                        int n = 10/0;
                        //记录日志
                        int flag = logInfoMapper.insert(logInfo);
                        if(flag>0 && n>=0){
                            return true;
                        }
                        return  false;
                    }
                }
                

                使用postman测试:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                运行结果:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                在运行结果中可以看到,会话只是释放了没有提交

                MySQL中的数据(原来的数据:zangsan–40;lisi–160):

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                可以看到数据没有发生改变

                事务的流程:

                运⾏程序, 发现数据库没有插⼊任何数据.

                流程描述:

                (1)Controller.updateUserInfo方法开始事务

                (2) userInfoService.updateUserInfo(logInfo)(开启一个事务,嵌套在Controller事务中,执⾏成功) (和Controller.updateUserInfo 使⽤同⼀个大事务)

                (3)记录操作⽇志, 插⼊⼀条数据(开启一个事务,嵌套在Controller事务中,出现异常, 执⾏失败) (和Controller.updateUserInfo 使⽤同⼀个大事务)

                (5)因为步骤3出现异常, 事务回滚;步骤2和3使⽤同⼀个事务, 所以步骤2的数据也回滚了.

                3.4 REQUIRED和NESTED的区别

                3.4.1 NESTED–某一嵌套事务回滚

                Controller和Service都使用NESTED

                logInfoService嵌套事务异常回滚:

                @Slf4j
                @Service
                public class LogInfoService {
                    @Autowired
                    private LogInfoMapper logInfoMapper;
                    @Transactional(propagation = Propagation.NESTED)
                    public boolean insert(LogInfo logInfo){
                         try {
                             int n = 10/0;
                             //记录日志
                             int flag = logInfoMapper.insert(logInfo);
                             if(flag>0 && n>=0){
                                 return true;
                             }
                         }catch (Exception e){
                             //手动回滚
                             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                             log.info("日志事务回滚");
                             return true;
                         }
                        return  false;
                    }
                }
                

                使用postman测试:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                运行结果:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                MySQL数据:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                log_info中的数据没有发生改变,但是user_info数据发生了改变

                流程描述:

                (1)Controller.updateUserInfo方法开始事务

                (2) userInfoService.updateUserInfo(logInfo)(开启一个事务,嵌套在Controller事务中,执⾏成功) (和Controller.updateUserInfo 使⽤同⼀个大事务)

                (3)记录操作⽇志, 插⼊⼀条数据(开启一个事务,嵌套在Controller事务中,出现异常并捕获,手动回滚事务) (和Controller.updateUserInfo 使⽤同⼀大个事务)

                (5)步骤3出现异常, 但是捕获了并手动回滚事务,没有发生异常;步骤2和3使⽤同⼀个事务, 步骤3相当于来说执行成功,所以步骤2的数据提交了。

                3.4.2 REQUIRED–部分事务回滚

                Controller和Service都使用REQUIRED

                logInfoService部分事务异常回滚:

                @Slf4j
                @Service
                public class LogInfoService {
                    @Autowired
                    private LogInfoMapper logInfoMapper;
                    @Transactional(propagation = Propagation.REQUIRED)
                    public boolean insert(LogInfo logInfo){
                         try {
                             int n = 10/0;
                             //记录日志
                             int flag = logInfoMapper.insert(logInfo);
                             if(flag>0 && n>=0){
                                 return true;
                             }
                         }catch (Exception e){
                             //手动回滚
                             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                             log.info("日志事务回滚");
                             return true;
                         }
                        return  false;
                    }
                }
                

                使用postman测试:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                运行结果:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                事务回滚后没有提交事务

                MySQL数据:

                【SpringBoot】Spring事务--详解@Transactional:rollbackFor、事务隔离级别(isolation)、事务传播机制(propagation))

                log_info和user_info中的数据没有发生改变

                流程描述:

                (1)Controller.updateUserInfo方法开始事务

                (2) userInfoService.updateUserInfo(logInfo)(加入Controller事务中,执⾏成功) (和Controller.updateUserInfo 使⽤同⼀个事务)

                (3)记录操作⽇志, 插⼊⼀条数据(加入Controller事务中,出现异常并捕获,手动回滚事务) (和Controller.updateUserInfo 使⽤同⼀个事务)

                (5)步骤3出现异常, 但是捕获了并手动回滚事务,没有发生异常;步骤2和3使⽤同⼀个事务, 在同一个事务中不能实现部分回滚,所以步骤2也回滚了。

                3.4.3 总结

                NESTED和REQUIRED区别:

                (1)整个事务如果全部执⾏成功, ⼆者的结果是⼀样的.

                (2)如果事务⼀部分执⾏成功, REQUIRED加⼊事务会导致整个事务全部回滚. NESTED嵌套事务可以实现局部回滚, 不会影响上⼀个⽅法中执⾏的结果.

                嵌套事务之所以能够实现部分事务的回滚, 是因为事务中有⼀个保存点(savepoint)的概念, 嵌套事务进⼊之后相当于新建了⼀个保存点, 而滚回时只回滚到当前保存点.

                资料参考: MySQL官方文档

                4. 总结

                1. Spring中使⽤事务, 有两种⽅式: 编程式事务(⼿动操作)和声明式事务. 其中声明式事务使⽤较多,在⽅法上添加 @Transactional 就可以实现了
                2. 通过 @Transactional(isolation = Isolation.SERIALIZABLE) 设置事务的隔离级别. Spring 中的事务隔离级别有 5 种
                3. 通过 @Transactional(propagation = Propagation.REQUIRED) 设置事务的传播机制, Spring 中的事务传播级别有 7 种, 重点关注 REQUIRED (默认值) 和 REQUIRES_NEW
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码