Spring源碼解析之事務傳播特性

一、使用方式

可以采用Transactional,配置propagation即可。
打開org.springframework.transaction.annotation.Transactional可見默認傳播特性是REQUIRED

/**
	 * The transaction propagation type.
	 * <p>Defaults to {@link Propagation#REQUIRED}.
	 * @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior()
	 */
	Propagation propagation() default Propagation.REQUIRED;

二、getTransaction

顧名思義,此項屬性是在事務存在交互時生效。那麼到底是如何生效的呢,核心源碼位於org.springframework.transaction.support.AbstractPlatformTransactionManager。核心入口是getTransaction方法。

@Override
	public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
		Object transaction = doGetTransaction();

		// Cache debug flag to avoid repeated checks.
		boolean debugEnabled = logger.isDebugEnabled();

		//
		if (definition == null) {
			// Use defaults if no transaction definition given.
			definition = new DefaultTransactionDefinition();
		}

		//已經存在事務 根據事務傳播特性進行處理
		if (isExistingTransaction(transaction)) {
			// Existing transaction found -> check propagation behavior to find out how to behave.
			return handleExistingTransaction(definition, transaction, debugEnabled);
		}
		// Check definition settings for new transaction.
		if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
			throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
		}

		// No existing transaction found -> check propagation behavior to find out how to proceed.
		//當前不存在事務 MANDATORY直接拋出異常 
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		}
		//REQUIRED  REQUIRES_NEW NESTED則會新建一個事務
		else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
			}
			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 ex) {
				resume(null, suspendedResources);
				throw ex;
			}
		}
		else {
			xxx
		}
	}

可以看到,在當前不存在事務時,

  • MANDATORY 直接拋出異常
  • REQUIRED REQUIRES_NEW NESTED 自動新建一個事務。

那麼存在事務時是如何處理的呢?

三、handleExistingTransaction

/**
	 * 處理已經存在事務的情況
	 * Create a TransactionStatus for an existing transaction.
	 */
	private TransactionStatus handleExistingTransaction(
			TransactionDefinition definition, Object transaction, boolean debugEnabled)
			throws TransactionException {

		//NEVER 已經存在事務 直接拋出異常
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
			throw new IllegalTransactionStateException(
					"Existing transaction found for transaction marked with propagation 'never'");
		}

		//NOT_SUPPORTED 註意prepareTransactionStatus方法參數傳遞事務的時候傳遞參數為null,所以是采用非事務方式運行。執行會掛起當前事務。
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
			if (debugEnabled) {
				logger.debug("Suspending current transaction");
			}
			Object suspendedResources = suspend(transaction);
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(
					definition, null, false, newSynchronization, debugEnabled, suspendedResources);
		}


		//REQUIRES_NEW 新建事務 會掛起當前事務
		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;
			}
		}

		//NESTED 處理嵌套事務
		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() + "]");
			}
			// 支持的話則采用Savepoint 否則開啟新事務 並不會掛起當前事務
			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.
				DefaultTransactionStatus status =
						prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
				//註意此處會創建savePoint
				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.
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				// 註意此處newTransaction屬性設置為true,說明確實采用瞭創建新事務方式來實現
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, null);
				doBegin(transaction, definition);
				prepareSynchronization(status, definition);
				return status;
			}
		}
		// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
		if (debugEnabled) {
			logger.debug("Participating in existing transaction");
		}
		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);
	}

可以看到,當已經存在事務時,

  • NEVER 直接報錯,不支持事務
  • NOT_SUPPORTED 默默按照非事務方式運行
  • REQUIRES_NEW 新建一個事務。
  • NESTED 處理嵌套事務 視情況采用savePoint或者新建事務。
  • 其他的 加入當前事務

四、NESTED 嵌套事務

SavePoint

先簡單說說SavePoint機制吧。這個也比較簡單。
比如一個 事務比較復雜,容易出錯。那麼如果當前DB支持SavePoint的話,那麼創建一個SavePoint就等於創建瞭一個快照,可以不用每次都回滾整個事務,僅回滾到指定的SavePoint即可。

五、個人理解

NESTED這個處理確實比較復雜。個人也查瞭很多資料。目前個人目前理解如下:
NESTED對於事務的處理主要在於級別不同。
REQUIRES_NEW創建的兩個事務是平級的,一個事務的成功與否對另一個事務的成功與否不產生影響。
而NESTED創建的事務則名副其實,是受其父級事務影響的。
一句話總結就是,子事務的成功與否不影響父級事務的成功,但是父級事務的成功與否則會影響子事務的成功。
父事務回滾,子事務一定會滾。
子事務回滾,父事務不一定會滾。

六、總結

最後總結如下

名稱 說明
PROPAGATION_REQUIRED 方法被調用時自動開啟事務,在事務范圍內使用則使用同一個事務,否則開啟新事務。 默認選項。
PROPAGATION_SUPPORTS 支持當前事務,如果當前沒有事務,就以非事務方式執行。
PROPAGATION_MANDATORY 支持當前事務,如果當前沒有事務,就拋出異常。
PROPAGATION_REQUIRES_NEW 新建事務,如果當前存在事務,把當前事務掛起。
PROPAGATION_NOT_SUPPORTED 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
PROPAGATION_NEVER 以非事務方式執行,如果當前存在事務,則拋出異常。
PROPAGATION_NESTED 如果一個活動的事務存在,則運行在一個嵌套的事務中. 如果沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。需要JDBC3.0以上支持。

到此這篇關於Spring源碼解析之事務傳播特性的文章就介紹到這瞭,更多相關Spring事務傳播特性內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: