【Spring】事务传播机制

事务传播机制

  • [一. 事务传播机制是什么](#一. 事务传播机制是什么)
  • [二. 为什么需要事务传播机制](#二. 为什么需要事务传播机制)
  • [三. 事务传播机制有哪些](#三. 事务传播机制有哪些)
  • [四. Spring 事务传播机制演示](#四. Spring 事务传播机制演示)
    • [1. ⽀持当前事务(REQUIRED)](#1. ⽀持当前事务(REQUIRED))
    • [2. 不支持当前事务(REQUIRES_NEW)](#2. 不支持当前事务(REQUIRES_NEW))
    • [3. 不⽀持当前事务,NEVER 抛异常](#3. 不⽀持当前事务,NEVER 抛异常)
    • [4. NESTED 嵌套事务](#4. NESTED 嵌套事务)

一. 事务传播机制是什么

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,先调用保存用户,再调用保存 日志

javascript 复制代码
@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;
    }
}

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

javascript 复制代码
@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);
    }
}

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

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

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

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

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

javascript 复制代码
@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;
    }
}

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

javascript 复制代码
@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);
    }
}

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

javascript 复制代码
@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;
    }
}

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

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

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

javascript 复制代码
@RestController
public class UserController {
    @Resource
    private UserService userService;
    
    @RequestMapping("/save")
    @Transactional(propagation = Propagation.REQUIRED)
    public Object save(User user) {
        // 插⼊⽤户操作
        userService.save(user);
        return true;
    }
}

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

javascript 复制代码
@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);
    }
}

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

4. NESTED 嵌套事务

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

javascript 复制代码
@RestController
public class UserController {
    @Resource
    private UserService userService;


    @RequestMapping("/save")
    @Transactional(propagation = Propagation.REQUIRED)
    public Object save(User user) {
        // 插⼊⽤户操作
        userService.save(user);
        return true;
    }
}

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

javascript 复制代码
@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;
    }
}

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

javascript 复制代码
@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;
    }
}

最终程序的执⾏结果:

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

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

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

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

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

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

注意:

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

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

相关推荐
盒马盒马1 分钟前
Redis:zset类型
数据库·redis
程序员是干活的2 分钟前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节
煸橙干儿~~12 分钟前
分析JS Crash(进程崩溃)
java·前端·javascript
2401_8543910813 分钟前
Spring Boot大学生就业招聘系统的开发与部署
java·spring boot·后端
Amor风信子14 分钟前
华为OD机试真题---跳房子II
java·数据结构·算法
静听山水16 分钟前
mysql语句执行过程
数据库·mysql
虽千万人 吾往矣34 分钟前
golang gorm
开发语言·数据库·后端·tcp/ip·golang
杨荧40 分钟前
【JAVA开源】基于Vue和SpringBoot的洗衣店订单管理系统
java·开发语言·vue.js·spring boot·spring cloud·开源
mariokkm1 小时前
Django一分钟:在Django中怎么存储树形结构的数据,DRF校验递归嵌套模型的替代方案
数据库·django·sqlite
陈逸轩*^_^*1 小时前
Java 网络编程基础
java·网络·计算机网络