日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring 事务说明

發(fā)布時間:2024/9/30 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring 事务说明 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
在 spring 中一共定義了六種事務(wù)傳播屬性, 如果你覺得看起來不夠直觀, 那么我來轉(zhuǎn)貼一個滿大街都有的翻譯
引用
l ? ? ? ? ?PROPAGATION_REQUIRED -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就新建一個事務(wù)。這是最常見的選擇。
l ? ? ? ? ?PROPAGATION_SUPPORTS -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。
l ? ? ? ? ?PROPAGATION_MANDATORY -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。
l ? ? ? ? ?PROPAGATION_REQUIRES_NEW -- 新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。
l ? ? ? ? ?PROPAGATION_NOT_SUPPORTED -- 以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
l ? ? ? ? ?PROPAGATION_NEVER -- 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
l ? ? ? ? ?PROPAGATION_NESTED -- 如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則進(jìn)行與PROPAGATION_REQUIRED類似的操作。

前六個策略類似于EJB CMT,第七個(PROPAGATION_NESTED)是Spring所提供的一個特殊變量。 它要求事務(wù)管理器或者使用JDBC 3.0 Savepoint API提供嵌套事務(wù)行為(如Spring的DataSourceTransactionManager)

在我所見過的誤解中, 最常見的是下面這種:
引用
假如有兩個業(yè)務(wù)接口 ServiceA 和 ServiceB, 其中 ServiceA 中有一個方法實(shí)現(xiàn)如下
/**
* 事務(wù)屬性配置為 PROPAGATION_REQUIRED
*/
void methodA() {
// 調(diào)用 ServiceB 的方法
ServiceB.methodB();
}
那么如果 ServiceB 的 methodB 如果配置了事務(wù), 就必須配置為 PROPAGATION_NESTED
這種想法可能害了不少人, 認(rèn)為 Service 之間應(yīng)該避免互相調(diào)用, 其實(shí)根本不用擔(dān)心這點(diǎn),PROPAGATION_REQUIRED 已經(jīng)說得很明白,如果當(dāng)前線程中已經(jīng)存在事務(wù), 方法調(diào)用會加入此事務(wù), 果當(dāng)前沒有事務(wù),就新建一個事務(wù), 所以 ServiceB#methodB() 的事務(wù)只要遵循最普通的規(guī)則配置為 PROPAGATION_REQUIRED 即可, 如果 ServiceB#methodB (我們稱之為內(nèi)部事務(wù), 為下文打下基礎(chǔ)) 拋了異常, 那么 ServiceA#methodA(我們稱之為外部事務(wù)) 如果沒有特殊配置此異常時事務(wù)提交 (即 +MyCheckedException的用法), 那么整個事務(wù)是一定要 rollback 的, 什么 Service 只能調(diào) Dao 之類的言論純屬無稽之談, spring 只負(fù)責(zé)配置了事務(wù)屬性方法的攔截, 它怎么知道你這個方法是在 Service 還是 Dao 里 ?


也就是說, 最容易弄混淆的其實(shí)是 PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED, 那么這兩種方式又有何區(qū)別呢? 我簡單的翻譯一下 Juergen Hoeller 的話 :
PROPAGATION_REQUIRES_NEW 啟動一個新的, 不依賴于環(huán)境的 "內(nèi)部" 事務(wù). 這個事務(wù)將被完全 committed 或 rolled back 而不依賴于外部事務(wù), 它擁有自己的隔離范圍, 自己的鎖, 等等. 當(dāng)內(nèi)部事務(wù)開始執(zhí)行時, 外部事務(wù)將被掛起, 內(nèi)務(wù)事務(wù)結(jié)束時, 外部事務(wù)將繼續(xù)執(zhí)行.
另一方面, PROPAGATION_NESTED 開始一個 "嵌套的" 事務(wù), 它是已經(jīng)存在事務(wù)的一個真正的子事務(wù). 嵌套事務(wù)開始執(zhí)行時, 它將取得一個 savepoint. 如果這個嵌套事務(wù)失敗, 我們將回滾到此 savepoint. 嵌套事務(wù)是外部事務(wù)的一部分, 只有外部事務(wù)結(jié)束后它才會被提交.
由此可見, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區(qū)別在于, PROPAGATION_REQUIRES_NEW 完全是一個新的事務(wù), 而 PROPAGATION_NESTED 則是外部事務(wù)的子事務(wù), 如果外部事務(wù) commit, 嵌套事務(wù)也會被 commit, 這個規(guī)則同樣適用于 roll back.
那么外部事務(wù)如何利用嵌套事務(wù)的 savepoint 特性呢, 我們用代碼來說話
代碼
ServiceA {
? ? /** ?
? ? ?* 事務(wù)屬性配置為 PROPAGATION_REQUIRED ?
? ? ?*/ ?
? ? void methodA() { ??
? ? ? ? ServiceB.methodB(); ??
? ? } ??
} ??
??
ServiceB {
? ? /** ?
? ? ?* 事務(wù)屬性配置為 PROPAGATION_REQUIRES_NEW ?
? ? ?*/ ? ?
? ? void methodB() { ??
? ? } ?
} ? ? ?
這種情況下, 因為 ServiceB#methodB 的事務(wù)屬性為 PROPAGATION_REQUIRES_NEW, 所以兩者不會發(fā)生任何關(guān)系, ServiceA#methodA 和 ServiceB#methodB 不會因為對方的執(zhí)行情況而影響事務(wù)的結(jié)果, 因為它們根本就是兩個事務(wù), 在 ServiceB#methodB 執(zhí)行時 ServiceA#methodA 的事務(wù)已經(jīng)掛起了 (關(guān)于事務(wù)掛起的內(nèi)容已經(jīng)超出了本文的討論范圍, 有時間我會再寫一些掛起的文章) .
那么 PROPAGATION_NESTED 又是怎么回事呢? 繼續(xù)看代碼
代碼
ServiceA {
? ? /** ?
? ? ?* 事務(wù)屬性配置為 PROPAGATION_REQUIRED ?
? ? ?*/ ?
? ? void methodA() { ??
? ? ? ? ServiceB.methodB(); ??
? ? } ?
} ??
??
ServiceB {
? ? /** ?
? ? ?* 事務(wù)屬性配置為 PROPAGATION_NESTED ?
? ? ?*/ ? ?
? ? void methodB() { ??
? ? } ??
} ? ? ?
現(xiàn)在的情況就變得比較復(fù)雜了, ServiceB#methodB 的事務(wù)屬性被配置為 PROPAGATION_NESTED, 此時兩者之間又將如何協(xié)作呢? 從 Juergen Hoeller 的原話中我們可以找到答案, ServiceB#methodB 如果 rollback, 那么內(nèi)部事務(wù)(即 ServiceB#methodB) 將回滾到它執(zhí)行前的 SavePoint(注意, 這是本文中第一次提到它, 嵌套事務(wù)中最核心的概念), 而外部事務(wù)(即 ServiceA#methodA) 可以有以下兩種處理方式:
1. 改寫 ServiceA 如下
代碼
ServiceA { ??
? ? ? ?
? ? /** ?
? ? ?* 事務(wù)屬性配置為 PROPAGATION_REQUIRED ?
? ? ?*/ ?
? ? void methodA() { ??
? ? ? ? try { ??
? ? ? ? ? ? ServiceB.methodB(); ??
? ? ? ? } catch (SomeException) { ??
? ? ? ? ? ? // 執(zhí)行其他業(yè)務(wù), 如 ServiceC.methodC(); ??
? ? ? ? } ??
? ? } ??
??
} ??
??
這種方式也是嵌套事務(wù)最有價值的地方, 它起到了分支執(zhí)行的效果, 如果 ServiceB.methodB 失敗, 那么執(zhí)行 ServiceC.methodC(), 而 ServiceB.methodB 已經(jīng)回滾到它執(zhí)行之前的 SavePoint, 所以不會產(chǎn)生臟數(shù)據(jù)(相當(dāng)于此方法從未執(zhí)行過), 這種特性可以用在某些特殊的業(yè)務(wù)中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都沒有辦法做到這一點(diǎn). (題外話 : 看到這種代碼, 似乎似曾相識, 想起了 prototype.js 中的 Try 函數(shù) )
2. 代碼不做任何修改, 那么如果內(nèi)部事務(wù)(即 ServiceB#methodB) rollback, 那么首先 ServiceB.methodB 回滾到它執(zhí)行之前的 SavePoint(在任何情況下都會如此), 外部事務(wù)(即 ServiceA#methodA) 將根據(jù)具體的配置決定自己是 commit 還是 rollback (+MyCheckedException).
上面大致講述了嵌套事務(wù)的使用場景, 下面我們來看如何在 spring 中使用 PROPAGATION_NESTED, 首先來看AbstractPlatformTransactionManager
代碼
/** ?
?* Create a TransactionStatus for an existing transaction. ?
?*/ ?
private TransactionStatus handleExistingTransaction( ??
? ? ? ? TransactionDefinition definition, Object transaction, boolean debugEnabled) ??
? ? ? ? throws TransactionException { ??
??
? ?... 省略 ??
??
? ? 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() + "]"); ??
? ? ? ? } ??
? ? ? ? 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 = ??
? ? ? ? ? ? ? ? ? ? newTransactionStatus(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. ??
? ? ? ? ? ? doBegin(transaction, definition); ??
? ? ? ? ? ? boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER); ??
? ? ? ? ? ? return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null); ??
? ? ? ? } ??
? ? } ??
} ??
?
一目了然
1. 我們要設(shè)置 transactionManager 的 nestedTransactionAllowed 屬性為 true, 注意, 此屬性默認(rèn)為 false!!!再看 AbstractTransactionStatus#createAndHoldSavepoint() 方法
代碼
/** ?
?* Create a savepoint and hold it for the transaction. ?
?* @throws org.springframework.transaction.NestedTransactionNotSupportedException ?
?* if the underlying transaction does not support savepoints ?
?*/ ?
public void createAndHoldSavepoint() throws TransactionException { ??
? ? setSavepoint(getSavepointManager().createSavepoint()); ??
} ??
可以看到 Savepoint 是 SavepointManager.createSavepoint 實(shí)現(xiàn)的, 再看 SavepointManager 的層次結(jié)構(gòu), 發(fā)現(xiàn)其 Template 實(shí)現(xiàn)是 JdbcTransactionObjectSupport, 常用的 DatasourceTransactionManager, HibernateTransactionManager 中的 TransactonObject 都是它的子類 :
?
1. ? ? ?JdbcTransactionObjectSupport 告訴我們必須要滿足兩個條件才能 createSavepoint :
2. ? ? ?java.sql.Savepoint 必須存在, 即 jdk 版本要 1.4+
3. ? ? ?Connection.getMetaData().supportsSavepoints() 必須為 true, 即 jdbc drive 必須支持 JDBC 3.0
確保以上條件都滿足后, 你就可以嘗試使用 PROPAGATION_NESTED 了. (全文完)

總結(jié)

以上是生活随笔為你收集整理的spring 事务说明的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。