• 【Spring】事务传播机制


    一. 事务传播机制是什么

    Spring 事务传播机制定义了多个包含了事务的⽅法,相互调⽤时,事务是如何在这些⽅法间进⾏传递的。

    二. 为什么需要事务传播机制

    事务隔离级别是保证多个并发事务执⾏的可控性的(稳定性的),⽽事务传播机制是保证⼀个事务在多个调⽤⽅法间的可控性的(稳定性的)。

    举个栗子:

    比如新冠病毒,它有不同的隔离⽅式(酒店隔离还是居家隔离),是为了保证疫情可控,然⽽在每个⼈的隔离过程中,会有很多个执⾏的环节,⽐如酒店隔离,需要负责⼈员运送、物品运送、消杀原⽣活区域、定时核算检查和定时送餐等很多环节,⽽事务传播机制就是保证⼀个事务在传递过程中是可靠性的,回到本身案例中就是保证每个⼈在隔离的过程中可控的。

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

    在这里插入图片描述

    ⽽事务传播机制解决的是⼀个事务在多个节点(⽅法)中传递的问题,如下图所示:
    方法 1 调用 方法 2, 方法 2 调用方法 3:

    在这里插入图片描述

    三. 事务传播机制有哪些

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

    1. Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。
    2. Propagation.SUPPORTS:如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
    3. Propagation.MANDATORY:(mandatory:强制性)如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。
    4. Propagation.REQUIRES_NEW:表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。
    5. Propagation.NOT_SUPPORTED:以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
    6. Propagation.NEVER:以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。
    7. Propagation.NESTED:如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。

    以上 7 种传播⾏为,可以根据是否⽀持当前事务分为以下 3 类:

    在这里插入图片描述

    以情侣关系为例来理解以上分类:

    在这里插入图片描述

    四. Spring 事务传播机制演示

    1. ⽀持当前事务(REQUIRED)

    先开启事务先成功插⼊⼀条⽤户数据,然后再执⾏⽇志报错,⽽在⽇志报错是发⽣了异常,观察 propagation = Propagation.REQUIRED 的执⾏结果。

    controller 层,传播机制为 REQUIRED,先调用保存用户,再调用保存 日志

    @RestController
    public class UserController {
        @Resource
        private UserService userService;
    
        @Resource
        private LogService logService;
    
        @RequestMapping("/save")
        @Transactional(propagation = Propagation.REQUIRED)
        public Object save(User user) {
            // 插⼊⽤户操作
            userService.save(user);
    
            // 插⼊⽇志
            logService.saveLog("⽤户插⼊:" + user.getUsername());
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    UserService ,传播机制为 REQUIRED,正常保存

    @Service
    public class UserService {
        @Resource
        private UserMapper userMapper;
        @Transactional(propagation = Propagation.REQUIRED)
        public int save(User user) {
            System.out.println("执⾏ save ⽅法.");
            return userMapper.save(user);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    LogService ,传播机制为 REQUIRED,抛异常,注意异常一定要捕获

    @Service
    public class LogService {
        @Resource
        private LogMapper logMapper;
        @Transactional(propagation = Propagation.REQUIRED)
        public int saveLog(String content) {
            int result = logMapper.saveLog(content);
            // 出现异常
            try {
                int i = 10 / 0;
            } catch (Exception e) {
                // 一定要捕捉异常
                // 然后手动回滚
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    执⾏结果:用户表和日志表都没有插⼊任何数据。

    1. UserService 中的保存⽅法正常执⾏完成。
    2. LogService 保存⽇志程序报错。
    3. 因为三个方法的事务传播机制使⽤的都是 REQURIED, UserService、 LogService 和 Controller 使用同一个事务,所以整个事务回滚。数据库中没有插⼊任何数据,也就是步骤 1 中的⽤户插⼊⽅法也回滚了。

    在这里插入图片描述

    2. 不支持当前事务(REQUIRES_NEW)

    UserController 类中的代码不变,将添加⽇志的⽅法修改为 REQUIRES_NEW 不⽀持当前事务,重新创建事务,观察执⾏结果:

    controller 层,传播机制为 REQUIRED,先调用保存用户,再调用保存 日志

    @RestController
    public class UserController {
        @Resource
        private UserService userService;
    
        @Resource
        private LogService logService;
    
        @RequestMapping("/save")
        @Transactional(propagation = Propagation.REQUIRED)
        public Object save(User user) {
            // 插⼊⽤户操作
            userService.save(user);
    
            // 插⼊⽇志
            logService.saveLog("⽤户插⼊:" + user.getUsername());
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    UserService ,传播机制为 REQUIRES_NEW,正常保存

    @Service
    public class UserService {
        @Resource
        private UserMapper userMapper;
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public int save(User user) {
            System.out.println("执⾏ save ⽅法.");
            return userMapper.save(user);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    LogService ,传播机制为 REQUIRES_NEW,抛异常, 注意异常一定要捕获

    @Service
    public class LogService {
        @Resource
        private LogMapper logMapper;
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public int saveLog(String content) {
            int result = logMapper.saveLog(content);
            // 出现异常
            try {
                int i = 10 / 0;
            } catch (Exception e) {
                // 一定要捕捉异常
                // 然后手动回滚
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    程序执⾏结果:User 表中成功插⼊了数据,Log 表执⾏失败,但没影响 UserController 中的事务。

    在这里插入图片描述

    3. 不⽀持当前事务,NEVER 抛异常

    controller 层,传播机制为 REQUIRED,只调用保存用户

    @RestController
    public class UserController {
        @Resource
        private UserService userService;
        
        @RequestMapping("/save")
        @Transactional(propagation = Propagation.REQUIRED)
        public Object save(User user) {
            // 插⼊⽤户操作
            userService.save(user);
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    UserService ,传播机制为 NEVER,正常保存

    @Service
    public class UserService {
        @Resource
        private UserMapper userMapper;
        @Transactional(propagation = Propagation.NEVER)
        public int save(User user) {
            System.out.println("执⾏ save ⽅法.");
            return userMapper.save(user);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    程序执⾏报错,⽤户表未添加任何数据。

    4. NESTED 嵌套事务

    controller 层,传播机制为 REQUIRED/NESTED/或者不写参数都行,调用保存用户

    @RestController
    public class UserController {
        @Resource
        private UserService userService;
    
    
        @RequestMapping("/save")
        @Transactional(propagation = Propagation.REQUIRED)
        public Object save(User user) {
            // 插⼊⽤户操作
            userService.save(user);
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    UserService ,传播机制为 NESTED ,正常保存

    @Service
    public class UserService {
        @Resource
        private UserMapper userMapper;
        
        @Resource
        private LogService logService;
        
        @Transactional(propagation = Propagation.NESTED)
        public int save(User user) {
            int result = userMapper.save(user);
            System.out.println("执⾏ save ⽅法.");
            // 调用插入插⼊⽇志的方法
            logService.saveLog("⽤户插⼊:" + user.getUsername());
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    LogService ,传播机制为 NESTED ,抛异常, 注意异常一定要捕获

    @Service
    public class LogService {
        @Resource
        private LogMapper logMapper;
    
        @Transactional(propagation = Propagation.NESTED)
        public int saveLog(String content) {
            int result = logMapper.saveLog(content);
            // 出现异常
            try {
                int i = 10 / 0;
            } catch (Exception e) {
                // 一定要捕捉异常
                // 然后手动回滚
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    最终程序的执⾏结果:
    ⽤户表中数据添加成功,⽇志表中没有添加任何数据,Log 中的事务已经回滚,但是嵌套事务不会回滚嵌套之前的事务,也就是说嵌套事务可以实现部分事务回滚。

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

    在这里插入图片描述

    嵌套事务(NESTED)和加⼊事务(REQUIRED )的区别:

    • 整个事务如果全部执⾏成功,⼆者的结果是⼀样的。
    • 如果事务执⾏到⼀半失败了,那么加⼊事务整个事务会全部回滚;⽽嵌套事务会局部回滚,不会影响上⼀个⽅法中执⾏的结果。

    嵌套事务(NESTED)和新建事务(REQUIRE_NEW )的区别:

    • 对于 NESTED,当外部事务回滚时,内部事务一定回滚。
    • 对于 REQUIRE_NEW,外部事务与内部事务之间是相互独立的,所以外部事务回滚时,内部事务不会回滚。

    注意:

    1. 当一个事务中出现异常时一定要将它 catch 然后手动回滚事务,如果不 catch 那么异常就会沿着调用链一直往上抛,这样只要是涉及到这个异常的所有事务都会回滚。
    2. 只有 NESTED 嵌套事务是类似于能同时存在多个事务,其他六种传播机制都是同一时刻只存在一个事务。
    3. 对于 NESTED,外部事务提交之前,内部事务无法提交,即外部事务回滚,内部事务一定回滚。

    好啦! 以上就是对 Spring 事务传播机制 的讲解,希望能帮到你 !
    评论区欢迎指正 !

  • 相关阅读:
    CAS真的无锁吗
    【Axure视频教程】旋转角度函数
    Softing为连接PROFIBUS网络提供多种接口产品方案
    【深度学习 Pytorch笔记 B站刘二大人 多输入模型 Multiple-Dimension 数学原理分析以及源码解读实现(6/10)】
    Apache网页优化
    Wireshark抓包分析ICMP协议
    再谈VC++动态链接库中的结构成员与VB或C#调用
    搅动PC市场风云,荣耀何以成为破局者?
    wordpress网站搭建(centos stream 9)
    Densenet--->比残差力度更大 senet-->本质抑制特征
  • 原文地址:https://blog.csdn.net/m0_61832361/article/details/133814387