Spring业务默许传达行为PROPAGATION_REQUIRED:怎样做到反常被捕获处理后业务还会回滚

前语

PROPAGATION_REQUIRED是Spring默许的业务传达机制,假如当时没有业务,就新建一个业务,假如当时现已存在一个业务,参加到当时业务。

话不多说,直接上代码。

场景代码:在办法testinsert(User user)insert_Exception(User user)办法上都添加了@Transactional,业务传达行为都是默许的PROPAGATION_REQUIRED

测验办法中业务默许会主动回滚,能够在测验办法上加@Rollback(value = false)修正装备

@Transactional
@Rollback(value = false)
@Test
public void test(){
    ...
    // 刺进一条记载,正常履行
    userService.insert(user1);
  	...
    try {
        // 刺进一条记载,履行过程中抛出一个RuntimeException
        userService.insert_Exception(user2);
    } catch (Exception e) {
        log.error("反常信息: {}", e.getMessage());
    }
}
public class UserService {
    ...
    @Transactional
    void insert(User user) {
        userMapper.insert(user);
    }
    @Transactional
    void insert_Exception(User user) {
        userMapper.insert(user);
        throw new RuntimeException("抛出反常");
    }
}

test履行成果:业务回滚,刺进记载均不成功。

信任定论我们早已知晓。

userService.insert(user1)履行正常,userService.insert_Exception(user2)履行过程中抛出反常,反常终究在test办法中被捕获,test办法履行正常,那么业务是怎样知道要回滚的?

解析

我们现已在测验办法上加了@Transactional注解,测验办法会被装备为在业务中运行,所以会履行startTransaction

有一个特点:flaggedForRollbackflaggedForRollback是业务的回滚标志,依据这个回滚标志,会立即强制提交或回滚装备测验上下文的业务。

// 为装备的测验上下文启动一个新的业务
void startTransaction() {
   ...
   // 现已加了@Rollback(value = false),业务不会主动回滚,值为false
   this.flaggedForRollback = this.defaultRollback;
   // 经过业务管理器获取当时存在的业务状况
   this.transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition);
   ...
}

经过业务管理器获取当时的业务状况transactionStatus,首要创立一个业务目标,经过TransactionSynchronizationManager获取connectionHolder,因为当时不存在业务,所以不存在connectionHolder

connectionHolder:包装JDBC连接的资源持有者,而spring的业务是经过数据库连接来完结的,所以敞开一个新业务时会创立一个数据源目标来表明ConnectionHolder,作为DataSourceTransactionManager的业务目标运用。

// 依据业务目标中的connectionHolder来判别当时是否存在业务
protected boolean isExistingTransaction(Object transaction) {
   DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
   // 业务目标中connectionHolder为空,回来false
   return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}

因为当时不存在业务,isExistingTransaction(Object transaction)履行成果回来false,且要敞开的业务传达行为是PROPAGATION_REQUIRED,最终会履行startTransaction办法,敞开新业务,符号当时业务状况transactionStatusnewTransactionnewConnectionHolder为true,表明当时是一个新业务,同时也会创立一个connectionHolder目标,该目标中包含了数据源的连接。

Spring事务默认传播行为PROPAGATION_REQUIRED:怎样做到异常被捕获处理后事务还会回滚

业务敞开后,代码履行到userService.insert(user1),首要获取当时的业务状况,履行doGetTransaction()获取当时业务状况的业务目标,因为当时现已存在业务,当时业务状况的业务目标中connectionHolder不为空,履行isExistingTransaction(Object transaction)回来true,进入handleExistingTransaction(def, transaction, debugEnabled)办法,且userService.insert(user1)的业务传达行为是PROPAGATION_REQUIRED,此刻不会敞开新业务,仅仅创立一个TransactionStatus来表明其当时的业务状况,其中newTransactionnewConnectionHolder为false,业务状况中的connectionHolder与敞开业务时创立的connectionHolder是同一个目标

Spring事务默认传播行为PROPAGATION_REQUIRED:怎样做到异常被捕获处理后事务还会回滚
userService.insert(user1)正常履行,然后进入commitTransactionAfterReturning(txInfo)

/**
 * 在成功完结调用后履行
 * txInfo -关于当时业务的信息
 */
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
   if (txInfo != null && txInfo.getTransactionStatus() != null) {
      ...
      txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
   }
}

commit办法中,userService.insert(user1)现已参加外围办法的业务,依据其业务状况会履行processCommit(defStatus)办法,这个办法会处理实践提交的业务,这儿业务没有实践提交,所以并没有履行任何操作。

以下是履行commit对业务状况的一些判别

// status 当时业务状况
public final void commit(TransactionStatus status) throws TransactionException {
   // 判别当时业务是否完结,便是说是否现已提交或者回滚
   if (status.isCompleted()) {
      throw new IllegalTransactionStateException(
            "Transaction is already completed - do not call commit or rollback more than once per transaction");
   }
   DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
   // 假如回滚标志现已设置为true,则开端回滚
   if (defStatus.isLocalRollbackOnly()) {
      ...
      processRollback(defStatus, false);
      return;
   }
   // shouldCommitOnGlobalRollbackOnly():回来是否以大局办法对已符号为仅回滚的业务调用doCommit,默许值:false。假如	    // rollbackOnly符号被设置为true,那么业务就会回滚而不会提交
   // defStatus.isGlobalRollbackOnly(),判别rollbackOnly的值
   if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
      ...
      }
      // 处理回滚操作
      processRollback(defStatus, true);
      return;
   }
   // 提交业务
   processCommit(defStatus);
}

持续履行到userService.insert_Exception(user2),这儿也会参加外围办法的业务,创立一个业务状况目标,操作和userService.insert(user1)一样,可是在办法履行过程中抛出反常,这儿不会履行commitTransactionAfterReturning,变成履行completeTransactionAfterThrowing(txInfo, ex)

开端处理回滚操作,履行doSetRollbackOnly办法,将ConnectionHolder的仅回滚符号rollbackOnly设置为为true,最终userService.insert_Exception(user2)抛出反常到外围办法,反常在外围办法中被捕获并处理。

Spring事务默认传播行为PROPAGATION_REQUIRED:怎样做到异常被捕获处理后事务还会回滚

这时因为现已处理了userService.insert_Exception(user2)抛出的反常,测验办法test是正常履行完结的,而且之前添加了@Rollback(value = false),将flaggedForRollback设置为false,业务并不会主动回滚,所以业务开端履行提交操作,可是在处理业务提交之前,会依据业务状况(status中的rollbackOnly)和大局业务的仅回滚符号(connectionHolder中的rollbackOnly)别离进行判别是否要进行回滚操作。

假如this.flaggedForRollback的值为true,那么在endTransaction()会直接履行rollback办法,不会履行commit办法,在rollback办法中会履行processRollback(defStatus, false),然后完结业务回滚。

// 依据回滚标志,立即强制提交或回滚装备测验上下文的业务
void endTransaction() {
   ...
   try {
      // 因为之前添加了@Rollback(value = false),将flaggedForRollback设置为false,这儿会履行提交办法
      if (this.flaggedForRollback) {
         this.transactionManager.rollback(this.transactionStatus);
      }
      else {
         this.transactionManager.commit(this.transactionStatus);
      }
   }
   ...
}
// status 当时业务状况
public final void commit(TransactionStatus status) throws TransactionException {
   ...
   DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
   // 经过业务状况的仅回滚标志来判别是否回滚
   if (defStatus.isLocalRollbackOnly()) {
      ...
      // 处理回滚操作,参数unexpected为false
      processRollback(defStatus, false);
      return;
   }
   // 经过判别大局业务自身被业务和谐器是否被符号为回滚
   if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
      ...经过查看业务目标确定仅回滚标志  
      // 处理回滚操作,参数unexpected为true
      processRollback(defStatus, true);
      return;
   }
   // 提交业务
   processCommit(defStatus);
}

在上面的代码中,有两处代码会判别仅回滚标志rollbackOnly,可是终究都会履行processRollback来回滚业务,那么processRollback(defStatus, false)processRollback(defStatus, true)会履行哪个办法,为什么它们的第二个参数(参数名:unexpected)又有什么不同呢?

首要解释一下为什么仅回滚标志rollbackOnly能决议现有业务的回滚

在业务管理器中,有一个特点globalRollbackOnParticipationFailure,默许值为true,用来设置是否在参与的业务失利后将现有业务大局符号为仅回滚。而在shouldCommitOnGlobalRollbackOnly()办法中,决议了是否以大局办法对已符号为仅回滚的业务调用doCommit,也便是履行提交操作,其默许值为false。

因此,在这种情况下(设置了仅回滚标志为true),业务管理器将触发回滚,然后抛出一个unexpectedRollbackexception

那么以上代码会履行哪个办法?

defStatus指的是test办法的业务状况,defStatus.isLocalRollbackOnly()查看的是defStatus的仅回滚符号rollbackOnly,test办法正常履行成功,业务状况中的仅回滚符号为false。

想一想在办法履行失利时是怎样设置仅回滚符号rollbackOnly的,没错,便是在connectionHolder,所以查看大局业务是否被符号为回滚便是查看connectionHolder中的仅回滚符号rollbackOnly

// defStatus.isGlobalRollbackOnly()的完结
@Override
public boolean isRollbackOnly() {
	return getConnectionHolder().isRollbackOnly();
}

为什么processRollback处理回滚操作,参数unexpected一个为true,一个为false?

unexpected,便是意外的意思。

  • processRollback(defStatus, true):test办法正常履行完结,原本要履行提交操作,可是大局业务自身被业务和谐器被符号为回滚,所以变为履行回滚操作,unexpected为true表明这是意外回滚。简而言之,原本要提交,最终回滚了,这便是意外回滚,所以最终也会抛出意外回滚反常。
  • processRollback(defStatus, false):办法履行过程中抛有反常未处理导致业务回滚时,unexpected为false,unexpected为为false,表明该回滚并不意外。

总结

一言以蔽之:在业务传达行为都是PROPAGATION_REQUIRED的情况下,假如办法敞开了新业务,首要会获取当时业务状况,没有connectionHolder就会创立一个,假如办法内部调用业务办法,被调用的办法都会参加当时业务,共用同一个connectionHolder,假如被调用的办法出现反常而导致回滚,就会设置connectionHolder中的仅回滚符号rollbackOnly为真,便是符号大局业务为仅回滚,这样就算在外围办法中处理了被调用办法抛出的反常,终究业务仍是会履行回滚操作。

OK,就这样吧,有过错的地方能够指出,欢迎讨论!