Spring源碼解析之事務(wù)傳播特性
一、使用方式
可以采用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
顧名思義,此項屬性是在事務(wù)存在交互時生效。那么到底是如何生效的呢,核心源碼位于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();
}
//已經(jīng)存在事務(wù) 根據(jù)事務(wù)傳播特性進行處理
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.
//當(dāng)前不存在事務(wù) MANDATORY直接拋出異常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
//REQUIRED REQUIRES_NEW NESTED則會新建一個事務(wù)
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
}
}
可以看到,在當(dāng)前不存在事務(wù)時,
- MANDATORY 直接拋出異常
- REQUIRED REQUIRES_NEW NESTED 自動新建一個事務(wù)。
那么存在事務(wù)時是如何處理的呢?
三、handleExistingTransaction
/**
* 處理已經(jīng)存在事務(wù)的情況
* Create a TransactionStatus for an existing transaction.
*/
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
//NEVER 已經(jīng)存在事務(wù) 直接拋出異常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
//NOT_SUPPORTED 注意prepareTransactionStatus方法參數(shù)傳遞事務(wù)的時候傳遞參數(shù)為null,所以是采用非事務(wù)方式運行。執(zhí)行會掛起當(dāng)前事務(wù)。
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 新建事務(wù) 會掛起當(dāng)前事務(wù)
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 處理嵌套事務(wù)
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 檢查是否支持嵌套事務(wù)
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 否則開啟新事務(wù) 并不會掛起當(dāng)前事務(wù)
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);
//注意此處會創(chuàng)建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屬性設(shè)置為true,說明確實采用了創(chuàng)建新事務(wù)方式來實現(xiàn)
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);
// 其他的傳播特性則加入當(dāng)前事務(wù) 不會創(chuàng)建新事務(wù)
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
可以看到,當(dāng)已經(jīng)存在事務(wù)時,
- NEVER 直接報錯,不支持事務(wù)
- NOT_SUPPORTED 默默按照非事務(wù)方式運行
- REQUIRES_NEW 新建一個事務(wù)。
- NESTED 處理嵌套事務(wù) 視情況采用savePoint或者新建事務(wù)。
- 其他的 加入當(dāng)前事務(wù)
四、NESTED 嵌套事務(wù)
SavePoint
先簡單說說SavePoint機制吧。這個也比較簡單。
比如一個 事務(wù)比較復(fù)雜,容易出錯。那么如果當(dāng)前DB支持SavePoint的話,那么創(chuàng)建一個SavePoint就等于創(chuàng)建了一個快照,可以不用每次都回滾整個事務(wù),僅回滾到指定的SavePoint即可。
五、個人理解
NESTED這個處理確實比較復(fù)雜。個人也查了很多資料。目前個人目前理解如下:
NESTED對于事務(wù)的處理主要在于級別不同。
REQUIRES_NEW創(chuàng)建的兩個事務(wù)是平級的,一個事務(wù)的成功與否對另一個事務(wù)的成功與否不產(chǎn)生影響。
而NESTED創(chuàng)建的事務(wù)則名副其實,是受其父級事務(wù)影響的。
一句話總結(jié)就是,子事務(wù)的成功與否不影響父級事務(wù)的成功,但是父級事務(wù)的成功與否則會影響子事務(wù)的成功。
父事務(wù)回滾,子事務(wù)一定會滾。
子事務(wù)回滾,父事務(wù)不一定會滾。
六、總結(jié)
最后總結(jié)如下
| 名稱 | 說明 |
| PROPAGATION_REQUIRED | 方法被調(diào)用時自動開啟事務(wù),在事務(wù)范圍內(nèi)使用則使用同一個事務(wù),否則開啟新事務(wù)。 默認選項。 |
| PROPAGATION_SUPPORTS | 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。 |
| PROPAGATION_MANDATORY | 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。 |
| PROPAGATION_REQUIRES_NEW | 新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。 |
| PROPAGATION_NOT_SUPPORTED | 以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。 |
| PROPAGATION_NEVER | 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。 |
| PROPAGATION_NESTED | 如果一個活動的事務(wù)存在,則運行在一個嵌套的事務(wù)中. 如果沒有活動事務(wù), 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執(zhí)行。需要JDBC3.0以上支持。 |
到此這篇關(guān)于Spring源碼解析之事務(wù)傳播特性的文章就介紹到這了,更多相關(guān)Spring事務(wù)傳播特性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一個applicationContext 加載錯誤導(dǎo)致的阻塞問題及解決方法
這篇文章主要介紹了一個applicationContext 加載錯誤導(dǎo)致的阻塞問題及解決方法,需要的朋友可以參考下2018-11-11
Springboot整合freemarker 404問題解決方案
這篇文章主要介紹了Springboot整合freemarker 404問題解決方案,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-05-05
Spring cloud gateway設(shè)置context-path服務(wù)路由404排查過程
這篇文章主要介紹了Spring cloud gateway設(shè)置context-path服務(wù)路由404排查過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08

