【老王读Spring Transaction-5】Spring事务传播行为propagation behavior的实现原理

前言

Spring 对事务的封装除了实现了基本的事务管理之外,还提供了事务传播行为(propagation behavior)的高级特性。

通常,事务范围内的所有代码都会在该事务中运行。但是,如果在事务上下文已经存在的情况下再去执行其他事务方法,则可以指定事务的传播行为
例如,代码可以在现有事务中继续运行(常见情况),或者可以暂停现有事务并创建新事务。

事务传播行为的作用是将多个事务操作进行合并或者隔离。

Spring 版本

spring-tx 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)

正文

Spring 中定义的事务传播行为的作用是将多个事务操作进行合并或者隔离。
举个例子,比如:两个事务方法 t1(), t2(),t1 调用了 t2,那么事务应当如何处理?
这里可以有多种处理方式,比如:

  • t1 开启一个事务,t2 在 t1 的事务里面执行;
  • t1 开启一个事务,t2 开启另外一个新事务;
  • t1 开启一个事务,t2 以非事务的方式执行;
    等等

对事务传播行为的理解可以参考官方文档: https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#tx-propagation

这种情况下,就需要使用到 Spring 的事务传播行为特性。

class AService {
   @Autowired
   BService bService;

   @Transactional
   public void t1() {
        ...
        bService.t2();
        ...
   }
}

class BService {

   @Transactional
   public void t2() {
        ...
   }
}

事务传播行为(propagation behavior)列举

Spring 中的事务传播行为共有 7 种,都是在 TransactionDefinition 中进行定义的:

 /** 【默认】支持当前事务;如果当前不存在事务,则创建一个新事务。(默认的事务传播行为)*/
 int PROPAGATION_REQUIRED = 0;

 /**
  * 【支持事务】支持当前事物,如果当前没有事物,就以非事物方式执行。
  * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
  * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#SYNCHRONIZATION_ON_ACTUAL_TRANSACTION
  */
 int PROPAGATION_SUPPORTS = 1;

 /** 【强制使用事务】使用当前的事物,如果当前不存在事物,就抛出异常。 */
 int PROPAGATION_MANDATORY = 2;

 /**
  * 【隔离多个事务】创建一个新事物,如果当前存在事物,把当前事物挂起。
  * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
  */
 int PROPAGATION_REQUIRES_NEW = 3;

 /**
  * 【强制非事务】不支持当前事务,始终以非事务方式执行。
  * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
  */
 int PROPAGATION_NOT_SUPPORTED = 4;

 /** 【不支持事务】不支持当前事务;如果当前事务存在,则抛出异常。 */
 int PROPAGATION_NEVER = 5;

 /**
  * 【嵌套事务】如果当前存在事物,则在嵌套事物内执行,支持按指定范围回滚事务(通过 savepoint 机制来实现);如果当前没有事物,新建一个事务。
  * 注意:嵌套事务的实际创建只适用于特定的事务管理器,比如 DataSourceTransactionManager 在使用 JDBC 3.0 driver 时。
  * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
  */
 int PROPAGATION_NESTED = 6;

org.springframework.transaction.TransactionDefinition
org.springframework.transaction.annotation.Propagation

事务传播行为归类

我们可以将事务传播行为归为三类:

  1. 支持当前事务
    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;

  2. 不支持当前事务
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;

  3. 嵌套事务
    int PROPAGATION_NESTED = 6;

详细的特性如下:

// 支持当前事务
int PROPAGATION_REQUIRED = 0;  // 【默认】支持当前事务;如果当前不存在事务,则创建一个新事务。
int PROPAGATION_SUPPORTS = 1;  // 【支持事务】支持当前事物,如果当前没有事物,就以非事物方式执行。
int PROPAGATION_MANDATORY = 2; // 【强制使用事务】使用当前的事物,如果当前没有事物,就抛出异常。 

// 不支持当前事务
int PROPAGATION_REQUIRES_NEW = 3;  // 【隔离多个事务】创建一个新事物,如果当前存在事物,把当前事物挂起。  
int PROPAGATION_NOT_SUPPORTED = 4; //  【强制非事务】不支持当前事务,始终以非事务方式执行。 
int PROPAGATION_NEVER = 5;  // 【不支持事务】以非事物方式执行,如果当前存在事物,则抛出异常。 

// 嵌套事务
int PROPAGATION_NESTED = 6; // 【嵌套事务】如果当前存在事物,则在嵌套事物内执行,支持按指定范围回滚事务(通过 savepoint 机制来实现);如果当前没有事物,新建一个事务。

Spring 是采用保存点(Savepoint)来实现 NESTED 嵌套事务的。Savepoint 可以指定范围提交事务。

事务传播行为的实现原理

org.springframework.transaction.annotation.Propagation 中的枚举值是 TransactionDefinition 中定义的常量。比如: TransactionDefinition.PROPAGATION_REQUIRES_NEW

我们可以通过找 TransactionDefinition.PROPAGATION_REQUIRES_NEW 被使用的地方,可以找到 AbstractPlatformTransactionManager#getTransaction()

getTransaction.png

首先,doGetTransaction() 会获取到一个事务对象,通常是 DataSourceTransactionObject
然后,会判断当前线程中是否存在事务

当在事务方法当中执行另外一个 @Transactional 标记的事务方法时,不同的事务传播行为的处理逻辑如下:
handleExistingTransaction.png

可以看到,如果当前线程中存在事务的话,在事务方法中再去调用另外一个 @Transactional 方法时,会根据不同的事务传播行为进行处理:
1、如果是 PROPAGATION_NEVER,则会直接抛出异常
2、如果是 PROPAGATION_NOT_SUPPORTED,则会将当前的事务挂起,再另起一个连接,以非事务的方式去执行
3、如果是 PROPAGATION_REQUIRES_NEW,则会将当前事务挂起,然后另起一个事务去执行
4、如果是 PROPAGATION_NESTED,则会以嵌套事务的方式执行,支持按指定范围回滚事务。(通过 savepoint 机制来实现)

小结

Spring 中定义的事务传播行为的作用是将多个事务操作进行合并或者隔离。
Spring 的事务传播行为可以分为三类:一,支持当前事务;二,不支持当前事务;三,嵌套事务。

// 支持当前事务
int PROPAGATION_REQUIRED = 0;  // 【默认】支持当前事务;如果当前不存在事务,则创建一个新事务。
int PROPAGATION_SUPPORTS = 1;  // 【支持事务】支持当前事物,如果当前没有事物,就以非事物方式执行。
int PROPAGATION_MANDATORY = 2; // 【强制使用事务】使用当前的事物,如果当前没有事物,就抛出异常。 

// 不支持当前事务
int PROPAGATION_REQUIRES_NEW = 3;  // 【隔离多个事务】创建一个新事物,如果当前存在事物,把当前事物挂起。  
int PROPAGATION_NOT_SUPPORTED = 4; //  【强制非事务】不支持当前事务,始终以非事务方式执行。 
int PROPAGATION_NEVER = 5;  // 【不支持事务】以非事物方式执行,如果当前存在事物,则抛出异常。 

// 嵌套事务
int PROPAGATION_NESTED = 6; // 【嵌套事务】如果当前存在事物,则在嵌套事物内执行,支持按指定范围回滚事务(通过 savepoint 机制来实现);如果当前没有事物,新建一个事务。

事务传播行为的实现是通过 TransactionStatus AbstractPlatformTransactionManager#getTransaction() 来实现的。
当 @Transactional 方法出现嵌套调用时,Spring 会根据不同的事务传播行为创建不同的 TransactionStatus,来控制是否使用同一个连接,是否共用同一个事务 或者 是不使用事务。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老王学源码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值