Spring事务的七种传播行为

1.事务的传播行为是什么?

事务的传播行为就是指多个声明了事务的方法相互调用的时候存在事务嵌套的问题,事务在方法中如何传播。
比如说:methodA()调用methodB(),两个方法都开启了事务,那么methodB()是开启了一个新事物,还是继续在methodA()这个事务中执行呢?就取决与事务的传播行为。
在这里插入图片描述
所以,Spring 为了解决这个问题,定义了 7 种事务传播行为。

传播行为含义
REQUIRED默认的Sring事务传播级别,如果当前事务存在,则加入该事务,如果不存在 就新建一个事务。
REQUIRES_NEW不管是否存在事务,都会新开启一个事务,新老事务相互独立;外部事务抛出异常回滚不会影响内部事务的正常提交。
NESTED如果当前存在事务,则嵌套在当前事务中执行,如果当前没有事务,则新建一个事务,类似与REQUIRE_NEW。
SUPPORTS表示支持当前事务,如果当前不存在事务,则以非事务的方式执行。
NOT_SUPPORTED表示以非事务的方式运行,如果当前存在事务,则把当前事务挂起。
MANDATORY表示强制事务执行,若当前不存在事务,则抛出异常。
NEVER表示以非事务的方式来运行,如果当前存在事务,则抛出异常。

2.具体传播行为

2.1 REQUIRED ,默认,存在事务则加入该事务,不存在则新建一个事务

当前存在事务则加入该事务,不存在事务则新建一个事务

示例代码:

	@Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
		userMapper.updateById(new TbUser(2, "李开", 33, 1));  // 更新用户
        methodB();		//调用methodB()
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodB() {
        userMapper.updateById(new TbUser(3, "李福", 22, 1));  //更新用户
        int a = 1/0;	// 发生异常
        System.out.println("=====执行 methodB 完成=====");
    }

结论:用户数据不会发生变化。当调用methodA()上下文没有事务就会开启一个事务,当执行methodB(),因为已经存在一个事务,methodB()不会开启新事务,而是加入到methodA()事务中执行。当‘int a = 1/0;’执行报错就会回滚methodA事务,而methodB也在methodA事务中,所有都会回滚。

2.2 REQUIRES_NEW,每次新开启事务,新老事务相互独立

不管是否存在事务,都会新开启一个事务,新老事务相互独立;外部事务抛出异常回滚不会影响内部事务的正常提交。

2.3 NESTED, 存在事务则嵌套当中执行,不存在事务则新建一个事务

如果一个活动的事务存在,则运行在一个嵌套事务中,如果没有活动事务,则按照PROPAGATION_REQUIRED属性执行。

2.4 SUPPORTS,存在则支持当前事务,不存在事务则以非事务执行。

如果存在事务则支持当前事务,如果不存在事务则无事务运行

2.5 NOT_SUPPORTED, 以非事务的方式运行,存在事务则把当前事务挂起

表示以非事务的方式运行,如果当前存在事务,则把当前事务挂起。

2.6 MANDATORY,强制事务执行,不存在事务则抛出异常。

如果存在一个事务,则事务的运行,没有事务则抛出异常。

2.7 NEVER,以非事务来运行,存在事务则抛出异常。

总是非事务的运行,如果存在一个活动的事务,则抛出异常。

3.事务失效的场景

在方法上添加@Transactional 注解后,Spring 会基于这个类生成一个代理对象,会将这个代理对象作为 bean,当使用这个代理对象的方法时,那么代理逻辑会先把事务的自动提交设置为 false,然后再去执行具体的业务逻辑,如果执行逻辑没有出现异常,那么代理逻辑就会将事务进行提交,如果执行逻辑出现了异常,那么则会将事务进行回滚。默认情况下会对 RuntimeException 和 Error 进行回滚。可以利用 @Transactional 注解中的 rollbackFor 属性进行配置异常信息。

3.1 非public修饰的方法

@Transactional 只能用于 public 的方法上,否则事务不会生效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。

3.2 自身调用

事务的管理是通过代理执行的方式生效的,如果是方法内部调用,没有经过 Spring 的代理类,就调用不到了

3.3 被 final、static 关键字修饰的类或方法

3.4 没有被 Spring 管理

没有注入到IOC容器的的类。

3.5 数据库不支持事务

比如 Mysql 的 Myisam 存储引擎是不支持事务的,只有 innodb存储引擎才支持。

3.6 异常被捕获

当异常被捕获后,而且没有再抛出,那么事务是不会回滚的。

3.7 异常类型错误

@Service
public class OrderServiceImpl implements OrderService 
    @Transactional
    public void updateOrder(Order order) {
        try {
            // update order
        } catch {
            throw new Exception("更新错误");
        }
    }
}

这样事务也是不生效的,因为默认回滚的是:RuntimeException 和 Error,如果想触发其他异常的回滚,需要在注解上配置一下,如通过指定@Transactional(rollbackFor = Exception.class)的方式进行全异常捕获。

3.8 多线程调用

Spring 的事务是通过数据库连接来实现,而数据库连接是放在 ThreadLocal 里面。同一个事务,只能用同一个数据库连接。而多线程场景下,拿到的数据库连接是不一样的。

### Spring 框架中的事务传播行为 #### PROPAGATION_REQUIRED 这是默认的行为。如果当前存在事务,则加入该事务;如果没有事务正在运行,则启动一个新的事务。此模式适用于大多数场景。 ```java @Transactional(propagation = Propagation.REQUIRED) public void methodA() { // 方法逻辑... } ``` 这种情况下,`methodB()` 将会参与到 `methodA()` 的同一个事务里[^1]。 #### PROPAGATION_SUPPORTS 如果有活动的事务就支持它,但是如果没有的话也不会开启新的事务而是以非事务方式执行业务操作。 ```java @Transactional(propagation = Propagation.SUPPORTS) public void nonTransactionalMethod() { // 非事务性的处理流程... } ``` 这意味着即使被调用了也没有强制要求一定要在一个事务上下文中工作[^2]。 #### PROPAGATION_MANDATORY 必须在一个已经存在的事务内运行,否则抛出异常。这通常用于那些总是期望在其父级组件所定义好的事务边界之内工作的服务层组件上。 ```java @Transactional(propagation = Propagation.MANDATORY) public void mandatoryTransactionMethod() throws Exception { // 只能在已有事务中执行的操作... } ``` 当尝试单独调用此类方法时将会失败并引发错误提示。 #### PROPAGATION_REQUIRES_NEW 无论是否存在现有事务都会创建一个全新的独立事务来包围目标对象的方法调用过程。新旧两个事物之间相互隔离互不影响彼此的状态变化情况。 ```java @Transactional(propagation = Propagation.REQUIRES_NEW) public void requiresNewTransactionMethod() { try { // 新建事务内的具体实现细节... } finally { // 即使外部事务回滚这里也会正常提交 } } ``` 对于像发送邮件这样的动作来说非常有用,在任何情形下都希望确保能够成功完成而不受其他因素干扰。 #### PROPAGATION_NOT_SUPPORTED 尽可能避免使用事务环境来进行数据库访问或其他资源控制等活动。如果确实有现成可用的事物则将其挂起直到本段程序结束为止再恢复原来状态继续前进下去。 ```java @Transactional(propagation = Propagation.NOT_SUPPORTED) public void notSupportedTransactionMethod() { // 不需要事务保护的任务... } ``` 这种方式适合读取只读数据或执行一些不涉及持久化变更的工作项。 #### PROPAGATION_NEVER 严格禁止任何形式下的事务参与进来。一旦检测到哪怕是最轻微的存在感也将立即终止整个进程并向上传播相应的异常信息给调用者知道发生了什么问题。 ```java @Transactional(propagation = Propagation.NEVER) public void neverAllowTransactionMethod() throws Exception { // 绝不允许在此处发生任何有关交易的事情... } ``` 这类配置很少见除非应用程序设计中有特殊需求才会考虑采用。 #### PROPAGATION_NESTED 允许嵌套式的子事务结构形成父子关系链路体系。内部的小型单元可以在更大范围的整体框架之下自主决定何时保存更改成果以及如何应对可能出现的各种意外状况而不会影响全局稳定性。 ```java @Transactional(propagation = Propagation.NESTED) public void nestedTransactionMethod() { // 子事务的具体实施策略... } ``` 比如在记录订单的同时还可以安全地更新库存数量等关联表单字段值却不必担心因为某一方出现问题而导致另一方也被迫跟着一起撤销之前所做的全部努力。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值