spring 事务源码分析(六)传播机制---NESTED

975 阅读2分钟

这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战

前言

上篇博客之后,本文主要是详解 NESTED这个事务传播机制

和前文一样的四个问题

  • 子事务提交,父事务会提交么?

  • 父事务提交,子事务会提交么?

  • 子事务回滚,父事务会回滚么?

  • 父事务会回滚,子事务会回滚么?

文本中所说的前文是:spring 事务源码(五),如果大家没有看过建议先看看,因为NESTEDREQUIRE很相似,只有一点不同

伪代码

最外层的事务

@Transactional
method_a(){
  
  method_b();
}

调用的子事务

@Transactional(propagation = Propagation.NESTED)
method_b(){
  
  
}

savePoint(备份点)

这里先说一个概念,savePoint,暂且称之为 备份点

是很多db都支持的一种机制,可以回滚到指定的备份点,类似操作系统的备份

image-20211102201306313

当事务指定回滚到备份点一的时候,那么发生在备份点一后的备份点二、三所做操作全部回滚

可以参考

源码分析

getTransaction

区别点在于

handleExistingTransaction

private TransactionStatus handleExistingTransaction(
      TransactionDefinition definition, Object transaction, boolean debugEnabled)
      throws TransactionException {
   .....
     if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			....
       // 是否保存备份点
			if (useSavepointForNestedTransaction()) {
				// 
				DefaultTransactionStatus status =
						prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
        // 创建一个备份点
				status.createAndHoldSavepoint();
				return status;
			}
			else {
				// 这种情况暂时不考虑,还没搞懂
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, null);
				doBegin(transaction, definition);
				prepareSynchronization(status, definition);
				return status;
			}
		}
     ......
}

image-20211102201505390

执行业务sql

因为并没有修改线程中的connect holder所以method_b 中的所有操作和method_a 是在同一个db事务

提交事务

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
   ....
     if (status.hasSavepoint()) {
            unexpectedRollback = status.isGlobalRollbackOnly();
            // 类似释放删除备份点
            status.releaseHeldSavepoint();
     }
  .....
}

image-20211102202316845

image-20211102202340919

回滚

private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
   ...
     // method_b 回滚执行此方法
      if (status.hasSavepoint()) {
            status.rollbackToHeldSavepoint();
         }
  .....
}

image-20211102202507912

此时回滚有多种可能,取决于业务代码

@Transactional(rollbackFor = Exception.class)
method_a(){
  
  method_b();
}

@Transactional(rollbackFor = Exception.class)
method_b(){
  
}

先说这种代码形式,

先是事务回滚到备份点,然后因为 抛出异常未捕获,method_a 也回滚,那么全部回滚

@Transactional(rollbackFor = Exception.class)
method_a(){
  
  try{
    method_b();
  }catch(){
    
  }
}

@Transactional(rollbackFor = Exception.class)
method_b(){
  
}

如果是这种形式的话,就只会回滚method_b 中的事务

REQUIRED_NEW结果是一样的