Spring事務(wù)管理器詳解
理解Spring事務(wù)抽象
Spring事務(wù)抽象的關(guān)鍵是事務(wù)策略的概念。事務(wù)策略是由TransactionManager定義的,特別是用于強制事務(wù)管理的
org.springframework.transaction.PlatformTransactionManager接口和用于響應(yīng)式事務(wù)管理的
org.springframework.transaction.ReactiveTransactionManager接口。下面的清單顯示了
PlatformTransactionManager API的定義:?
這主要是一個服務(wù)提供者接口(SPI),盡管你可以從應(yīng)用程序代碼中以編程方式使用它。因為
PlatformTransactionManager是一個接口,所以可以根據(jù)需要輕松模擬或存根。它沒有綁定到查找策略,比如JNDI。PlatformTransactionManager實現(xiàn)的定義類似于Spring Framework IoC容器中的任何其他對象(或bean)。
同樣,為了與Spring的理念保持一致,可以由PlatformTransactionManager接口的任何方法拋出的TransactionException是未檢查的(也就是說,它擴(kuò)展了java.lang.
exception.RuntimeException類)。事務(wù)基礎(chǔ)結(jié)構(gòu)失敗幾乎總是致命的。在極少數(shù)情況下,應(yīng)用程序代碼實際上可以從事務(wù)失敗中恢復(fù),應(yīng)用程序開發(fā)人員仍然可以選擇捕獲和處理TransactionException。不過對于開發(fā)人員并沒有強制這樣做。
getTransaction(TransactionDefinition)方法返回一個TransactionStatus對象,這取決于TransactionDefinition參數(shù)。如果當(dāng)前調(diào)用堆棧中存在匹配的事務(wù),則返回的TransactionStatus可以表示一個新的事務(wù),也可以表示一個現(xiàn)有的事務(wù)。后一種情況的含義是,與Java EE事務(wù)上下文一樣,TransactionStatus與執(zhí)行線程相關(guān)聯(lián)。
從Spring Framework 5.2開始,Spring還為使用響應(yīng)式類型或Kotlin協(xié)程的響應(yīng)式應(yīng)用程序提供了事務(wù)管理抽象。下面的清單顯示了
org.springframework.transaction.ReactiveTransactionManager定義的事務(wù)策略:?
TransactionDefinition接口具體說明:
Propagation(傳播特性):通常,事務(wù)范圍內(nèi)的所有代碼都在該事務(wù)中運行。但是,如果在事務(wù)上下文已經(jīng)存在時運行事務(wù)方法,則可以指定該行為。例如,代碼可以繼續(xù)在現(xiàn)有事務(wù)中運行(常見情況),也可以暫停現(xiàn)有事務(wù)并創(chuàng)建新事務(wù)。
Isolation(隔離性):該事務(wù)與其他事務(wù)的工作隔離的程度。例如,這個事務(wù)能看到其他事務(wù)未提交的寫嗎?
Timeout(超時時間):該事務(wù)在超時和被底層事務(wù)基礎(chǔ)結(jié)構(gòu)自動回滾之前運行的時間。
Read-Only(只讀狀態(tài)):當(dāng)代碼讀取但不修改數(shù)據(jù)時,可以使用只讀事務(wù)。在某些情況下,例如使用Hibernate時,只讀事務(wù)可能是一種有用的優(yōu)化。
這些設(shè)置反映了標(biāo)準(zhǔn)的事務(wù)概念。如有必要,請參考討論事務(wù)隔離級別和其他核心事務(wù)概念的資源。理解這些概念對于使用Spring框架或任何事務(wù)管理解決方案都是至關(guān)重要的。
TransactionStatus接口為事務(wù)代碼提供了一種控制事務(wù)執(zhí)行和查詢事務(wù)狀態(tài)的簡單方法。這些概念應(yīng)該很熟悉,因為它們對所有事務(wù)api都很常見。下面的清單顯示了TransactionStatus接口:?
無論在Spring中選擇聲明式事務(wù)管理還是編程式事務(wù)管理,定義正確的TransactionManager實現(xiàn)都是絕對必要的。通常通過依賴注入來定義此實現(xiàn)。
TransactionManager實現(xiàn)通常需要了解它們工作的環(huán)境:JDBC、JTA、Hibernate等等。下面的示例展示了如何定義本地
PlatformTransactionManager實現(xiàn)(在本例中,使用普通JDBC)。
首先,你的先定義一個數(shù)據(jù)源:
Java Config:?
相關(guān)的
PlatformTransactionManager bean定義隨后具有對DataSource定義的引用。它應(yīng)該類似于下面的例子:
Java Config:?
Hibernate事務(wù)配置
還可以輕松地使用Hibernate本地事務(wù),如以下示例所示。在這種情況下,需要定義一個Hibernate LocalSessionFactoryBean,應(yīng)用程序代碼可以使用它來獲取Hibernate會話實例。
DataSource bean定義類似于前面顯示的本地JDBC示例
本例中的txManager bean屬于HibernateTransactionManager類型。就像DataSourceTransactionManager需要對DataSource的引用一樣,HibernateTransactionManager也需要對SessionFactory的引用。下面的例子聲明了sessionFactory和txManager bean:
LocalSessionFactoryBean是個FactoryBean,同時實現(xiàn)了InitializingBean接口,所以在當(dāng)前類初始化的時候,會調(diào)用afterPropertiesSet方法,該方法中會初始化SessionFactory對象。
將資源與事務(wù)同步
現(xiàn)在應(yīng)該清楚了如何創(chuàng)建不同的事務(wù)管理器,以及如何將它們鏈接到需要同步到事務(wù)的相關(guān)資源(例如,DataSourceTransactionManager到JDBC DataSource,Hibernate TransactionManager到Hibernate SessionFactory,等等)。本節(jié)描述了應(yīng)用程序代碼(直接或間接地,通過使用JDBC、Hibernate或JPA等持久性API)如何確保正確創(chuàng)建、重用和清理這些資源。本節(jié)還討論了如何(可選地)通過相關(guān)的TransactionManager觸發(fā)事務(wù)同步。
- 高級同步方法
首選方法是使用Spring的最高級別基于模板的持久性集成API,或者使用具有事務(wù)感知工廠bean或代理的本地ORM API來管理本地資源工廠。這些事務(wù)感知解決方案在內(nèi)部處理資源的創(chuàng)建和重用、清理、資源的可選事務(wù)同步以及異常映射。因此,用戶數(shù)據(jù)訪問代碼不必處理這些任務(wù)。通常,使用本機(jī)ORM API,或者通過使用JdbcTemplate采用模板方法進(jìn)行JDBC訪問。
- 低級同步方法
DataSourceUtils(用于JDBC)、EntityManagerFactoryUtils(適用于JPA)、SessionFactoryUtil(適用于Hibernate)等類存在于較低級別。當(dāng)您希望應(yīng)用程序代碼直接處理本機(jī)持久性API的資源類型時,可以使用這些類來確保獲得正確的Spring Framework托管實例,同步事務(wù)(可選),并將過程中發(fā)生的異常正確映射到一致的API。
例如,在JDBC的情況下,你可以使用Spring的org.springframework.jdbc.datasource.DataSourceUtils類,而不是在數(shù)據(jù)源上直接調(diào)用getConnection()方法,如下所示:?
如果現(xiàn)有事務(wù)已經(jīng)有一個同步(鏈接)到它的連接,則返回該實例。否則,方法調(diào)用將觸發(fā)新連接的創(chuàng)建,該連接(可選地)同步到任何現(xiàn)有事務(wù),并可用于同一事務(wù)中的后續(xù)重用。如前所述,任何SQLException都被包裝在Spring框架
CannotGetJdbcConnectionException中,這是Spring框架中未檢查的DataAccessException類型的層次結(jié)構(gòu)之一。這種方法提供了比從SQLException輕松獲得的更多信息,并確保了跨數(shù)據(jù)庫、甚至跨不同持久性技術(shù)的可移植性。
這種方法在沒有Spring事務(wù)管理的情況下也可以工作(事務(wù)同步是可選的),因此無論是否使用Spring進(jìn)行事務(wù)管理,都可以使用它。
當(dāng)然,一旦你使用了Spring的JDBC支持、JPA支持或Hibernate支持,你通常不喜歡使用DataSourceUtils或其他輔助類,因為你更喜歡通過Spring抽象工作而不是直接使用相關(guān)的api。例如,如果使用Spring JdbcTemplate或jdbc。對象包來簡化你的JDBC使用,正確的連接檢索發(fā)生在幕后,你不需要編寫任何特殊的代碼。
- TransactionAwareDataSourceProxy
在最底層存在
TransactionAwareDataSourceProxy類。這是目標(biāo)DataSource的代理,它封裝目標(biāo)DataSource以添加對Spring托管事務(wù)的感知。
除非必須調(diào)用現(xiàn)有代碼并傳遞標(biāo)準(zhǔn)JDBC DataSource接口實現(xiàn),否則幾乎你不需要或不想使用這個類。在這種情況下,這段代碼可能是可用的,但參與了spring管理的事務(wù)。你可以使用前面提到的高級抽象來編寫新代碼。
聲明式事務(wù)管理
Spring Framework的聲明式事務(wù)管理是通過Spring面向方面編程(AOP)實現(xiàn)的。然而,由于事務(wù)方面的代碼是隨Spring Framework一起提供的,并且可以以樣板的方式使用,因此通常不需要理解AOP概念就可以有效地使用這些代碼。
僅僅告訴你使用@Transactional注解注釋的類,將@
EnableTransactionManagement添加到你的配置中,并期望你理解它是如何工作的,這是不夠的。為了加深理解,本節(jié)將在與事務(wù)相關(guān)的問題上下文中解釋Spring框架的聲明式事務(wù)基礎(chǔ)結(jié)構(gòu)的內(nèi)部工作原理。
關(guān)于Spring Framework的聲明性事務(wù)支持,需要掌握的最重要的概念是,這種支持是通過AOP代理啟用的,并且事務(wù)Advice是由元數(shù)據(jù)驅(qū)動的(目前是基于XML或注釋的)。AOP與事務(wù)元數(shù)據(jù)的組合產(chǎn)生了一個AOP代理,該代理使用TransactionInterceptor和適當(dāng)?shù)腡ransactionManager實現(xiàn)來圍繞方法調(diào)用驅(qū)動事務(wù)。
Spring Framework的TransactionInterceptor為命令式和反應(yīng)式編程模型提供事務(wù)管理。攔截器通過檢查方法返回類型來檢測所需的事務(wù)管理風(fēng)格。返回響應(yīng)式類型(如Publisher或Kotlin Flow(或其子類型))的方法符合響應(yīng)式事務(wù)管理的條件。包括void在內(nèi)的所有其他返回類型都使用代碼路徑進(jìn)行強制事務(wù)管理。
事務(wù)管理會影響所需的事務(wù)管理器。強制事務(wù)需要PlatformTransactionManager,而響應(yīng)事務(wù)使用ReactiveTransactionManager實現(xiàn)。
@Transactional通常使用
PlatformTransactionManager管理的線程綁定事務(wù),將事務(wù)暴露給當(dāng)前執(zhí)行線程中的所有數(shù)據(jù)訪問操作。注意:這不會傳播到方法中新啟動的線程。由
ReactiveTransactionManager管理的反應(yīng)事務(wù)使用Reactor上下文而不是線程本地屬性。因此,所有參與的數(shù)據(jù)訪問操作都需要在同一反應(yīng)管道中的同一Reactor上下文中執(zhí)行。
下圖顯示了在事務(wù)代理上調(diào)用方法的概念視圖:

- 聲明性事務(wù)實現(xiàn)示例
考慮以下接口及其附屬實現(xiàn)。本例使用Foo和Bar類作為占位符,這樣你就可以專注于事務(wù)的使用,而不必關(guān)注特定的域模型。就本例而言,DefaultFooService類在每個實現(xiàn)方法的主體中拋出
UnsupportedOperationException實例是好的。該行為允許你查看正在創(chuàng)建的事務(wù),然后回滾以響應(yīng)UnsupportedOperationException實例。FooService接口如下所示:?
接口實現(xiàn):?
以FooService接口的前兩個方法getFoo(String)和getFoo為例配置事務(wù)攔截。?
<aop:config/>定義確保txAdvice bean定義的事務(wù)建議在程序中的適當(dāng)點運行。首先,定義一個切入點,該切入點與FooService接口(fooServiceOperation)中定義的任何操作的執(zhí)行相匹配。然后使用advisor將切入點與txAdvice關(guān)聯(lián)起來。結(jié)果表明,在執(zhí)行fooServiceOperation時,運行由txAdvice定義的通知。
一個常見的要求是使整個服務(wù)層具有事務(wù)性。做到這一點的最佳方法是更改切入點表達(dá)式以匹配服務(wù)層中的任何操作。以下示例顯示了如何執(zhí)行此操作:?
- 回滾聲明性事務(wù)
上面介紹了如何在應(yīng)用程序中以聲明方式為類(通常是服務(wù)層類)指定事務(wù)設(shè)置的基礎(chǔ)知識。本節(jié)描述如何在XML配置中以簡單的聲明式方式控制事務(wù)的回滾。
向Spring框架的事務(wù)基礎(chǔ)設(shè)施指示事務(wù)的工作要回滾的推薦方法是從當(dāng)前在事務(wù)上下文中執(zhí)行的代碼拋出異常。Spring框架的事務(wù)基礎(chǔ)結(jié)構(gòu)代碼在調(diào)用堆棧中彈出氣泡時捕獲任何未處理的異常,并確定是否將事務(wù)標(biāo)記為回滾。
在默認(rèn)配置中,Spring框架的事務(wù)基礎(chǔ)結(jié)構(gòu)代碼僅在運行時未檢查異常的情況下將事務(wù)標(biāo)記為回滾。也就是說,當(dāng)拋出的異常是RuntimeException的實例或子類時。(默認(rèn)情況下,錯誤實例也會導(dǎo)致回滾)。從事務(wù)方法拋出的已檢查異常不會導(dǎo)致默認(rèn)配置中的回滾。
你可以通過指定回滾規(guī)則,準(zhǔn)確地配置哪些Exception類型標(biāo)記要回滾的事務(wù),包括已檢查的異常。
回滾規(guī)則
回滾規(guī)則確定在拋出給定異常時是否應(yīng)該回滾事務(wù),這些規(guī)則基于模式。模式可以是完全限定類名,也可以是異常類型(必須是Throwable的子類)的完全限定類名的子字符串,目前不支持通配符。例如,"
javax.servlet.ServletException"或"ServletException"將匹配javax.servlet.ServletException及其子類。可以通過Rollback-for和no-rollback-for屬性在XML中配置回滾規(guī)則,這些屬性允許將模式指定為字符串。當(dāng)使用@Transactional時,可以通過rollbackFor/noRollbackFor和
rollbackForClassName/noRollbackForClassName屬性來配置回滾規(guī)則,這些屬性允許分別將模式指定為類引用或字符串。當(dāng)異常類型被指定為類引用時,其全限定名將用作模式。因此,@Transactional(rollbackFor = example.CustomException.class)等價于@Transactional(rollbackForClassName = "example.CustomException")。
以下XML片段演示了如何通過回滾for屬性提供異常模式,為已檢查的、特定于應(yīng)用程序的異常類型配置回滾:?
如果不希望在引發(fā)異常時回滾事務(wù),也可以指定“無回滾”規(guī)則。下面的示例告訴Spring Framework的事務(wù)基礎(chǔ)結(jié)構(gòu)即使面對未處理的
InstrumentNotFoundException也要提交附帶事務(wù):?
當(dāng)Spring Framework的事務(wù)基礎(chǔ)設(shè)施捕捉到異常并參考配置的回滾規(guī)則以確定是否將事務(wù)標(biāo)記為回滾時,最強的匹配規(guī)則獲勝。因此,在以下配置的情況下,
InstrumentNotFoundException以外的任何異常都會導(dǎo)致伴隨事務(wù)的回滾:?
你還可以通過編程方式指示所需的回滾。盡管這個過程很簡單,但它具有很強的侵入性,并將您的代碼緊密地耦合到Spring Framework的事務(wù)基礎(chǔ)設(shè)施。以下示例顯示了如何以編程方式指示所需的回滾:?
























