深入理解spring如何管理事務(wù)
Spring 事務(wù)管理概述
最先需要明確一點的是,事務(wù)只是數(shù)據(jù)庫中的一個概念,我們的java程序和數(shù)據(jù)庫進行連接,java程序只是作為數(shù)據(jù)庫的客戶端,本質(zhì)是通過網(wǎng)絡(luò)連接對象connection來間接的操作數(shù)據(jù)庫對sql進行事務(wù)開啟,提交或者回滾。在 Spring 中,事務(wù)管理通常分為兩種方式:編程式事務(wù)和聲明式事務(wù)。其中,聲明式事務(wù)(基于注解 @Transactional
)是最常用、最直觀的做法。本質(zhì)上,Spring 通過 AOP(面向切面編程) 或 代理機制 來攔截方法調(diào)用,在方法執(zhí)行前后自動開啟、提交或回滾事務(wù)。
1. Spring 事務(wù)管理的整體架構(gòu)
1.1 PlatformTransactionManager接口
- Spring 為各種數(shù)據(jù)訪問框架提供了不同實現(xiàn)的 PlatformTransactionManager,例如:
DataSourceTransactionManager
(JDBC)JpaTransactionManager
(JPA/Hibernate)HibernateTransactionManager
(Hibernate)JpaTransactionManager
(JPA)以及其他第三方框架的事務(wù)管理器。
- 這些事務(wù)管理器對底層資源(數(shù)據(jù)庫連接、Session 等)進行事務(wù)控制,Spring 通過它們來統(tǒng)一管理事務(wù)的開啟、提交與回滾。
1.2 事務(wù)配置(Transaction Configuration)
- 通過注解
@EnableTransactionManagement
或者在 XML 中聲明<tx:annotation-driven>
,開啟 Spring 對聲明式事務(wù)的支持。 - 配置好對應(yīng)的
PlatformTransactionManager
Bean,讓 Spring 知道使用哪一種事務(wù)管理器。
1.3 聲明式事務(wù)(@Transactional)
- Spring 在運行時掃描被
@Transactional
注解標記的類或方法,自動為其生成 AOP 代理。 - 當外部調(diào)用該方法時,會先進入 事務(wù)切面,根據(jù)注解信息決定是否開啟事務(wù),執(zhí)行完畢后再根據(jù)方法執(zhí)行結(jié)果決定提交還是回滾。
1.4 事務(wù)增強(Advice)
- 事務(wù)的開啟、提交、回滾等邏輯,就封裝在 Spring 提供的事務(wù)增強(
TransactionInterceptor
)中,通過 AOP 動態(tài)攔截目標方法調(diào)用。
2. Spring 事務(wù)管理的關(guān)鍵流程
以下主要針對 聲明式事務(wù)(基于注解)進行說明:
2.1 Bean 初始化階段
- Spring 在啟動時,會掃描所有的 Bean,看它們是否使用了
@Transactional
注解。 - 如果某個類或方法使用了
@Transactional
,則由 Spring AOP 或 AspectJ 生成一個代理對象來代替原始對象。 - 代理對象內(nèi)部持有對真實對象的引用,同時嵌入事務(wù)管理邏輯。
2.2 方法調(diào)用攔截(AOP 代理)
- 當外部代碼調(diào)用該 Bean 的某個帶有
@Transactional
的方法時,實際上先調(diào)用到 代理對象。 - 代理對象 會進行一系列判斷,包括:
- 讀取
@Transactional
的屬性(如propagation
、isolation
、timeout
、readOnly
等); - 判斷當前線程是否已經(jīng)存在事務(wù)(如果有,則根據(jù)
propagation
屬性決定如何處理); - 如果沒有事務(wù)或者需要新建事務(wù),則調(diào)用
PlatformTransactionManager
的getTransaction(...)
開啟一個事務(wù)。
- 讀取
2.3 執(zhí)行目標方法
在事務(wù)已開啟(或已決定加入現(xiàn)有事務(wù))的前提下,代理對象通過反射調(diào)用目標 Bean 的真實方法。目標方法中執(zhí)行數(shù)據(jù)庫操作(或其他資源操作),如果拋出異常,Spring 會捕獲并根據(jù) rollback rules(例如:RuntimeException
、Error
默認回滾等)決定是否回滾。
2.4 提交或回滾事務(wù)
- 如果目標方法正常結(jié)束,Spring 在退出代理方法前,會調(diào)用
PlatformTransactionManager
的commit(...)
方法,提交事務(wù); - 如果出現(xiàn)了異常且符合回滾條件,Spring 調(diào)用
PlatformTransactionManager
的rollback(...)
方法,回滾事務(wù); - 此后,方法調(diào)用才真正返回給調(diào)用者。
2.5 釋放資源
- 事務(wù)提交或回滾之后,底層數(shù)據(jù)庫連接、Session 等資源會被釋放或歸還到連接池。
- Spring 也會在內(nèi)部進行相應(yīng)的清理操作,確保線程上下文不再持有錯誤的事務(wù)信息。
3. 事務(wù)屬性與配置
在使用 @Transactional
注解時,可以指定多個屬性來控制事務(wù)行為,比如:
@Transactional( propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 30, readOnly = false, rollbackFor = {Exception.class}, noRollbackFor = {CustomException.class} ) public void doSomething() { // ... }
propagation - 事務(wù)傳播級別
常見值:REQUIRED
、REQUIRES_NEW
、SUPPORTS
、MANDATORY
、NESTED
等,決定當前方法是否需要在已有事務(wù)中執(zhí)行,或新開一個事務(wù)等。
isolation - 事務(wù)隔離級別
如 READ_UNCOMMITTED
, READ_COMMITTED
, REPEATABLE_READ
, SERIALIZABLE
,決定事務(wù)間讀寫數(shù)據(jù)的可見性,防止臟讀、不可重復(fù)讀、幻讀等。
timeout - 超時時間
指定事務(wù)執(zhí)行的最大時間(秒),超過時間未完成則拋出超時異常并回滾。
readOnly - 只讀標志
指示該事務(wù)主要用于查詢操作,可能在某些情況下允許數(shù)據(jù)庫做額外優(yōu)化;某些數(shù)據(jù)庫會忽略此配置。
rollbackFor / noRollbackFor - 回滾策略
指定哪些異常拋出時應(yīng)回滾或不回滾;默認對 運行時異常 和 錯誤 回滾,對編譯異常 不回滾。
4. 實際開發(fā)中事務(wù)的常見問題
同類方法相互調(diào)用失效
如果在方法 A 內(nèi)部調(diào)用方法 B,而 B 也使用了 @Transactional
,但這兩者都屬于同一個類,則 B 的事務(wù)不會被代理攔截,導致事務(wù)注解失效??梢酝ㄟ^將 B 提取到另一個 Bean 或使用 AspectJ 等方式解決。
異常類型導致事務(wù)不回滾
Spring 默認對 RuntimeException
或 Error
回滾,而對受檢異常(Checked Exception)不回滾,需要在注解中顯式指定 rollbackFor
。不小心拋出了“錯誤類型”的異常,就可能導致事務(wù)回滾意外。
讀寫操作混用導致性能或數(shù)據(jù)一致性問題@Transactional(readOnly = true)
僅作提示,不會強制阻止寫操作。如果在此事務(wù)中執(zhí)行了寫操作或忘記設(shè)置 readOnly = false
,就可能引發(fā)數(shù)據(jù)不一致或性能問題。
事務(wù)傳播屬性配置不當
如果不小心將一個需要新事務(wù)的操作配置為 Propagation.REQUIRED
而不是 REQUIRES_NEW
,或者反之,則會導致事務(wù)邊界不符預(yù)期,進而出現(xiàn)臟數(shù)據(jù)、鎖競爭等問題。
大事務(wù)導致鎖競爭
在實際項目里,如果單次事務(wù)持續(xù)時間過長,可能會長時間占用數(shù)據(jù)庫鎖,導致鎖競爭或死鎖;需注意將大事務(wù)拆分或優(yōu)化為多次小事務(wù)。
多線程/異步場景下無法共享事務(wù)
Spring 的默認事務(wù)機制基于 ThreadLocal
綁定連接,異步任務(wù)或多線程無法復(fù)用同一事務(wù),如果你在一個方法里開啟多線程進行并發(fā)數(shù)據(jù)庫操作,子線程無法直接繼承主線程的事務(wù)上下文,會拿不到同一個 Connection。
5.spring聲明式事務(wù)執(zhí)行過程示例圖
6.總結(jié)
核心原理
Spring 通過 AOP 代理 攔截帶有 @Transactional
的方法,在方法執(zhí)行前后,根據(jù)注解屬性和底層 PlatformTransactionManager
自動開啟、提交或回滾事務(wù)。
關(guān)鍵組件
@Transactional
注解- 事務(wù)切面(
TransactionInterceptor
) PlatformTransactionManager
TransactionSynchronizationManager
(這是 Spring 用于管理“線程上下文”的關(guān)鍵工具類,記錄當前線程關(guān)聯(lián)的事務(wù)資源,如Connect。
同一事務(wù)內(nèi),所有數(shù)據(jù)庫操作都共享此 Connection)
事務(wù)配置
可自定義傳播級別、隔離級別、超時、回滾策略等,以滿足不同的業(yè)務(wù)需要。
到此這篇關(guān)于spring如何管理事務(wù)的文章就介紹到這了,更多相關(guān)spring管理事務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java設(shè)計模式編程中簡單工廠與抽象工廠模式的使用實例
這篇文章主要介紹了Java設(shè)計模式編程中簡單工廠與抽象工廠模式的使用實例,簡單工廠與抽象工廠都可以歸類于設(shè)計模式中的創(chuàng)建型模式,需要的朋友可以參考下2016-04-04fasterxml jackson反序列化時對于非靜態(tài)內(nèi)部類報錯問題及解決
這篇文章主要介紹了fasterxml jackson反序列化時對于非靜態(tài)內(nèi)部類報錯問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08spring?boot如何配置靜態(tài)路徑詳解(404出現(xiàn)的坑)
這篇文章主要給大家介紹了關(guān)于spring?boot如何配置靜態(tài)路徑的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2022-02-02淺談SpringMVC的攔截器(Interceptor)和Servlet 的過濾器(Filter)的區(qū)別與聯(lián)系 及Spr
這篇文章主要介紹了淺談SpringMVC的攔截器(Interceptor)和Servlet 的過濾器(Filter)的區(qū)別與聯(lián)系 及SpringMVC 的配置文件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07IDEA?高版本?PlantUML?插件默認主題修改的詳細過程
PlantUML 是非常不錯的使用腳本畫圖的工具,效率很高,很多人會選擇在 IDEA 中安裝 PlantUML Integration 插件,這篇文章主要介紹了IDEA?高版本?PlantUML?插件默認主題修改,需要的朋友可以參考下2022-09-09java應(yīng)用開發(fā)之Mybatis通過Mapper代理自定義接口的實現(xiàn)
這篇文章主要介紹了java應(yīng)用開發(fā)之Mybatis通過Mapper代理自定義接口的實現(xiàn)方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-09-09