Spring : Spring @Transactional-嵌套事物回滾

1.美圖

2.概述

事務回滾參考:Spring : Spring @Transactional-事物回滾

嵌套事務參考:Spring: 事務傳播機制

3.前文回顧

// 2.如果當前已經存在事物
// 重點:
// 如果當前已經存在啓動的事物,則根據本次要新建的事物傳播特性進行評估,以決定對新事物的後續處理
if (isExistingTransaction(transaction)) {
    // Existing transaction found -> check propagation behavior to find out how to behave.
    return handleExistingTransaction(definition, transaction, debugEnabled);
}

從這開始就要涉及到事物的嵌套處理了(在AbstractPlatformTransactionManager類的getTransaction方法中)。

因爲我們在在BankServicesave方法中調用了AccountService、PersonService的方法。那麼當BankService調用到另外兩個Servcie方法的時候,因爲當前已經存在事物(BankService),那麼接下來就要根據新事物的傳播特性,以決定下一步的處理了。先來感受一下處理過程:

/**
	 * Create a TransactionStatus for an existing transaction.
	 */
	private TransactionStatus handleExistingTransaction(
			TransactionDefinition definition, Object transaction, boolean debugEnabled)
			throws TransactionException {

		// 1.PROPAGATION_NEVER --> 以非事物方式執行,如果當前存在事物,則拋出異常。
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
			throw new IllegalTransactionStateException(
					"Existing transaction found for transaction marked with propagation 'never'");
		}

		// 2.以非事物方式執行,如果當前存在事物,則掛起當前事物。
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
			if (debugEnabled) {
				logger.debug("Suspending current transaction");
			}
			// 重點:掛起已有事物
			Object suspendedResources = suspend(transaction);
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			// 創建新事物,注意:transaction參數爲null,所以這裏創建的不是一個真正的事物
			return prepareTransactionStatus(
					definition, null, false, newSynchronization, debugEnabled, suspendedResources);
		}

		//3.新建事物,如果當前已經存在事物,則掛起當前事物。
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
			if (debugEnabled) {
				logger.debug("Suspending current transaction, creating new transaction with name [" +
						definition.getName() + "]");
			}
			// 掛起已有事物
			SuspendedResourcesHolder suspendedResources = suspend(transaction);
			try {
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				// 創建事物
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
				// 開啓事物
				doBegin(transaction, definition);
				// 初始化事物同步屬性
				prepareSynchronization(status, definition);
				return status;
			}
			catch (RuntimeException | Error beginEx) {
				resumeAfterBeginException(transaction, suspendedResources, beginEx);
				throw beginEx;
			}
		}

		// 4.如果當前存在事物,則在嵌套事物內執行;如果當前沒有事物,則與PROPAGATION_REQUIRED傳播特性相同
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			// 如果不允許嵌套事物,則拋出異常
			if (!isNestedTransactionAllowed()) {
				throw new NestedTransactionNotSupportedException(
						"Transaction manager does not allow nested transactions by default - " +
						"specify 'nestedTransactionAllowed' property with value 'true'");
			}
			if (debugEnabled) {
				logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
			}
			/**
			 * 下面對JtaTransactionManager和AbstractPlatformTransactionManager分別進行處理
			 */

			// useSavepointForNestedTransaction(),是否爲嵌套事務使用保存點
			// 1.對於JtaTransactionManager-->返回false
			// 2.對於AbstractPlatformTransactionManager-->返回true
			if (useSavepointForNestedTransaction()) {
				// Create savepoint within existing Spring-managed transaction,
				// through the SavepointManager API implemented by TransactionStatus.
				// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
				// 創建保存點在現有spring管理事務,通過TransactionStatus SavepointManager API實現。
				// 通常使用JDBC 3.0保存點。永遠不要激活Spring同步。
				DefaultTransactionStatus status =
						prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
				// 創建保存點
				status.createAndHoldSavepoint();
				return status;
			}
			else {
				// Nested transaction through nested begin and commit/rollback calls.
				// Usually only for JTA: Spring synchronization might get activated here
				// in case of a pre-existing JTA transaction.
				// 通過嵌套的開始,提交調用,及回滾調用進行嵌套事務。
				// 只對JTA有效,如果已經存在JTA事務,這裏可能會激活Spring同步。
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, null);
				doBegin(transaction, definition);
				prepareSynchronization(status, definition);
				return status;
			}
		}

		// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
		// 處理PROPAGATION_SUPPORTS和PROPAGATION_REQUIRED兩種傳播特性
		// PROPAGATION_REQUIRED --> 如果當前沒有事物,則新建一個事物;如果已經存在一個事物,則加入到這個事物中。
		// PROPAGATION_SUPPORTS --> 支持當前事物,如果當前沒有事物,則以非事物方式執行。
		if (debugEnabled) {
			logger.debug("Participating in existing transaction");
		}
		// 對於PROPAGATION_SUPPORTS和PROPAGATION_REQUIRED
		// 新事物參與已有事物時,是否驗證已有事物.此屬性值默認爲false;
		// 如開啓將驗證新事物和已有事物的隔離級別和事物只讀屬性是否相同
		if (isValidateExistingTransaction()) {
			// 驗證事物隔離級別
			// 如果當前事物的隔離級別不爲默認隔離級別,則比較當前事物隔離級別與已有事物隔離級別,
			// 如不同,則拋出事物隔離級別不兼容異常
			if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
				Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
				if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
					Constants isoConstants = DefaultTransactionDefinition.constants;
					throw new IllegalTransactionStateException("Participating transaction with definition [" +
							definition + "] specifies isolation level which is incompatible with existing transaction: " +
							(currentIsolationLevel != null ?
									isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
									"(unknown)"));
				}
			}
			// 驗證事物只讀屬性
			// 如果當前事物可寫,但是已有的事物是隻讀,則拋出異常
			if (!definition.isReadOnly()) {
				if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
					throw new IllegalTransactionStateException("Participating transaction with definition [" +
							definition + "] is not marked as read-only but existing transaction is");
				}
			}
		}
		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
		return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
	}

4.processRollback回顧

注意:這裏最外層的事物一定要開啓,如果將最外層的事物特性設置爲PROPAGATION_NOT_SUPPORTED,則不會引發嵌套事物的問題。

/**
	 * Process an actual rollback.
	 * The completed flag has already been checked.
	 * @param status object representing the transaction
	 * @throws TransactionException in case of rollback failure
	 *
	 * 執行回滾
	 */
	private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
		try {
			boolean unexpectedRollback = unexpected;

			try {
				// 1.事物回滾前調用事物同步接口
				triggerBeforeCompletion(status);

				// 2.如果有保存點,則回滾到保存點
				if (status.hasSavepoint()) {
					if (status.isDebug()) {
						logger.debug("Rolling back transaction to savepoint");
					}
					status.rollbackToHeldSavepoint();
				}
				// 3.如果當前事物是一個新的事物,則調用doRollback執行給定事物的回滾
				else if (status.isNewTransaction()) {
					if (status.isDebug()) {
						logger.debug("Initiating transaction rollback");
					}
					doRollback(status);
				}
				else {
					// Participating in larger transaction
					// 4.如果當前事物並非獨立事物,則將當前事物的rollbackOnly屬性標記爲true,等到事物鏈完成之後,一起執行回滾

					// 如果當前存在事物,但是事物的rollbackOnly屬性已經被標記爲true
					// 或者globalRollbackOnParticipationFailure(返回是否僅在參與事務失敗後纔將現有事務全局標記爲回滾)爲true
					if (status.hasTransaction()) {
						if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
							if (status.isDebug()) {
								logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
							}
							// 則將ConnectionHolder中的rollbackOnly標記爲true
							doSetRollbackOnly(status);
						}
						// 5.如果當前不存在事物,則不執行任何操作
						else {
							if (status.isDebug()) {
								logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
							}
						}
					}
					else {
						logger.debug("Should roll back transaction but cannot - no transaction available");
					}
					// Unexpected rollback only matters here if we're asked to fail early
					if (!isFailEarlyOnGlobalRollbackOnly()) {
						unexpectedRollback = false;
					}
				}
			}
			catch (RuntimeException | Error ex) {
				// 6.事物回滾後調用事物同步接口
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				throw ex;
			}

			triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);

			// Raise UnexpectedRollbackException if we had a global rollback-only marker
			if (unexpectedRollback) {
				throw new UnexpectedRollbackException(
						"Transaction rolled back because it has been marked as rollback-only");
			}
		}
		finally {
			// 7.事物完成後清理資源
			cleanupAfterCompletion(status);
		}
	}

5.恢復掛起事物

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
    // 1.將當前事物狀態標記爲已完成
    status.setCompleted();
    // 2.清除synchronization
    if (status.isNewSynchronization()) {
        TransactionSynchronizationManager.clear();
    }
    // 3.事務完成後清理資源。
    if (status.isNewTransaction()) {
        doCleanupAfterCompletion(status.getTransaction());
    }
    // 4.從嵌套事物中恢復被掛起的資源
    if (status.getSuspendedResources() != null) {
        if (status.isDebug()) {
            logger.debug("Resuming suspended transaction after completion of inner transaction");
        }
        Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
        resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
    }
}

關於其他的資源清理涉及的代碼很多,留在後面介紹,這裏我們只看如何恢復被掛起的事物。

參考:https://blog.csdn.net/lyc_liyanchao/article/details/85273628

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章