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!
推薦閱讀:
- Spring事務執行流程及如何創建事務
- Spring事務捕獲異常後依舊回滾的解決
- Spring超詳細講解事務
- Spring事務處理Transactional,鎖同步和並發線程
- Spring中propagation的7種事務配置及說明