Spring 事务传播行为浅析

  • Spring 在 TransactionDefinition 接口中规定了 7 种类型的事务传播行为
  • 事务传播行为是Spring框架独有的事务增强特性,不属于数据库行为
  • 通过这 7 种 传播行为,可以使我们更灵活的控制哪些行为在同一个事务内、哪些行为不在同一事务内、各事务之间的关系,从而使得我们的业务逻辑更加灵活

1 Spring中七种事务传播行为

  • 事务传播行为规定了在一个嵌套方法内部,哪些方法属于同一个事务,哪些方法不属于同一个事务,以及事务之间的关系
事务传播行为类型 说明
PROPAGATION_REQUIRED 如果存在则加入事务。如果不存在,创建新事务
PROPAGATION_SUPPORTS 如果存在则加入事务。如果不存在,以不可用事务执行
PROPAGATION_MANDATORY 如果存在则加入事务。如果不存在,抛出异常
PROPAGATION_REQUIRES_NEW 如果存在则挂起当前事务,并创建新事务。如果不存在则创建事务
PROPAGATION_NESTED 如果存在则创建嵌套事务。如果不存在,创建新事务
PROPAGATION_NOT_SUPPORTED 以不可用事务运行。如果存在事务,则先挂起当前事务
PROPAGATION_NEVER 以不可用事务运行。如果存在事务,抛出异常

2 定义

  • null:表示没有 @Transactional 注解的方法,且不在事务内
  • NULL:表示没有 @Transactional 注解的方法,但在事务内(其外部有事务,所以加入外部事务)
  • required:表示有 @Transactional(propagation = Propagation.REQUIRED) 的方法,但是外部没有事务
  • REQUIRED:表示有 @Transactional(propagation = Propagation.REQUIRED) 的方法,但是外部有事务
  • 同理其它 6 个传播行为
  • 数据库错误:表示有 @Transactional 注解的方法,执行数据库操作时失败,但是异常最终被 catch 掉了
  • 异常:方法抛出来的、没有被捕获的异常
  • 方法被回滚:表示方法内部的数据库操作被回滚
  • 事务切面方法:表示加入事务,且加入事务切面(被 @Transactional 注解注释的方法才会加入事务切面)
  • 事务方法:表示加入事务的方法(包含事务切面方法)
  • 外层事务:表示先加入同一事务的方法
  • 真实事务:表示有 @Transactional 注解的方法
  • 普通方法:没有 @Transactional 注解的方法

3 定理

  • null 方法不受事务影响,只要执行成功就不会被回滚(例:插入了一行数据后,执行其它语句立马报错也不会被回滚,因为null 方法插入后会立马提交,其 auto-commit 为 true)
  • 事务内发生 异常,加入这个事务的所有数据库操作都会被回滚

4 推理

  1. NULL 方法受事务影响,外层事务被回滚,NULL 方法也会被回滚(…方法证明)

  2. 如果 NULL 方法发生 数据库错误,不影响事务(即是 3)

  3. required 内部的 NULL 方法发生 数据库错误,required 方法不会被回滚(虽然在同一事务内,但是 NULL 方法的数据库异常不会传递到事务切面,因为 NULL 方法虽然加入了事务,但是没有加入事务切面)

  4. required 内部的 REQUIRED 方法发生 数据库错误,required 方法被回滚(通过事务切面将回滚状态传递到外层)

  5. required 内部的 SUPPORTS 方法发生 数据库错误,required 方法被回滚(同理)

  6. required 内部的 MANDATORY 方法发生 数据库错误,required 方法被回滚(同理)

  7. required 内部的 REQUIRES_NEW 方法发生 数据库错误,required 方法不会被回滚(不在同一个事务)

  8. required 内部的 NESTED 方法发生 数据库错误,required 方法不会被回滚(NESTED 发生数据库错误会回滚到保存点,保证了外部事务的有效性)

  9. required 内部的 NOT_SUPPORTED 方法发生 数据库错误,required 方法不会被回滚(NOT_SUPPORTED 方法 不在事务内)

  10. requires_new、nested 情况和 required 相同,因为这两个都只是开启新事务,意思和 required 一模一样

  11. supports、not_supported、never 都是以非事务运行和 null 效果一样(null之内的普通方法不能复用session)

  12. mandatory 直接抛异常

  13. NESTED:--------0------0---------,两个0 之间是嵌套事务,不管是内层嵌套事务还是外层事务,其实是一个事务,只是在这个事务线上创建了一个保存点,嵌套事务内部事情处理完了之后,整个事务状态会回到保存点时的状态,这样保证了即使嵌套内部发生数据库错误(这时仍然会设置事务为 rollback 状态),因为方法完了之后事务状态会回到保存点,所以 rollback 状态被覆盖了

  14. NESTED 和 NULL 的区别

    • NULL 内部的 事务方法 属于 NULL 所在事务,而 NESTED 内部的 事务方法 属于 NESTED 开启的嵌套事务和外层事务没关系
    1. NULL 内部的 事务切面方法 发生 数据库错误 会导致外部事务回滚,而 NESTED 内部的事务切面方法 发生 数据库错误 只会导致 NESTED 开启的嵌套事务回滚,而外层事务不会
  15. NESTED 和 REQUIRES_NEW 的区别

    • 外层事务发生 数据库错误 会导致 NESTED 方法回滚,而不会导致 REQUIRES_NEW 方法回滚

5 证明

5.1 事务传播行为证明

5.2 推理证明


6 事务切面设置的几个核心状态

6.1 newTransaction

  • 是否是新开启的事务

  • 此属性是直接赋值的,没有判断逻辑

  • 所有的小写/REQUIRES_NEW 为 true

  • 所有的大写(除了 REQUIRES_NEW) 为 false

  • 事务关闭时如果 newTransaction= true 且 actualTransactionActive=true,才会做 connection 的 commit 操作

6.2 actualNewSynchronization

  • 当前线程是否是新开启同步的

  • 如果当前线程已经开启同步了,返回 false。否则,返回 true

AbstractPlatformTransactionManager#newTransactionStatus
actualNewSynchronization = !TransactionSynchronizationManager.isSynchronizationActive()

6.3 isSynchronizationActive/actualTransactionActive

  • 当前线程是否同步/当前线程的事务是否可用

  • 如果 actualNewSynchronization 为 false 的话

    • 如果 status 中的 transaction 为 null,actualTransactionActive=false。否则为 true
    • isSynchronizationActive = true
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
   
    if (status.isNewSynchronization()) {
   
        //关键 1:设置当前线程事务是否活跃
        setActualTransactionActive(status.hasTransaction());
        //关键 2: 设置事务是否已经同步
        //synchronizations.set(new LinkedHashSet<>());
        initSynchronization();
    }
}

6.4 connectionHolder

  • 此属性只有在创建新的、可用的事务时,才会设置
  • transaction.newConnectionHolder = true
  • conHolder.setSynchronizedWithTransaction(true)
  • conHolder.setTransactionActive(true)
  • connection.setAutoCommit(false)
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
在 doBegin 方法中做了以下操作
	1. 创建 connectionHolder
    2. 将此 connectionHolder 设置到当前事务中,并设置 newConnectionHolder = true
    3. 设置 ConnectionHolder().setSynchronizedWithTransaction(true)  ----> 1
    4. 将此 connectionHolder 中的 connection 设置 auto-commit = false -----> 2 
    5. 设置 事务的 read only 状态(如果指定了 read only 的话
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值