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