Springboot @Transactional使用時(shí)需注意的幾個(gè)問題記錄
一、事務(wù)的隔離級(jí)別
在Springboot應(yīng)用中,如果我們想實(shí)現(xiàn)方法一旦執(zhí)行有異常產(chǎn)生,就觸發(fā)事務(wù)回滾,可以在方法上面添加@Transactional注解。如果應(yīng)用采用mysql數(shù)據(jù)庫,雖然mysql本身也有事務(wù)隔離機(jī)制,但在Sping+數(shù)據(jù)庫的應(yīng)用中,會(huì)以Spring事務(wù)為準(zhǔn)。mysql定義的事務(wù)隔離級(jí)別為可重復(fù)讀,在使用 Spring Boot 和 MySQL 的組合時(shí),如果你不特別指定隔離級(jí)別,那么實(shí)際使用的將是 MySQL 的默認(rèn)值 REPEATABLE READ。如果在一些特定場景中不想使用可重復(fù)讀,可通過@Transactional注解的isolation屬性來指定。isolation支持的選項(xiàng)有:
- ISOLATION_DEFAULT:使用后端數(shù)據(jù)庫默認(rèn)的隔離級(jí)別。
- ISOLATION_READ_UNCOMMITTED:最低的隔離級(jí)別,允許讀取尚未提交的數(shù)據(jù)變更,可能會(huì)導(dǎo)致臟讀、不可重復(fù)讀或幻讀。
- ISOLATION_READ_COMMITTED:允許讀取已經(jīng)提交的數(shù)據(jù),可以防止臟讀,但可能會(huì)出現(xiàn)不可重復(fù)讀或幻讀。
- ISOLATION_REPEATABLE_READ:對同一字段的多次讀取結(jié)果是一致的,除非數(shù)據(jù)是被當(dāng)前事務(wù)本身所修改,可以防止臟讀和不可重復(fù)讀,但幻讀仍可能發(fā)生。
- ISOLATION_SERIALIZABLE:最高的隔離級(jí)別,完全服從 ACID 的隔離級(jí)別,所有的事務(wù)依次逐個(gè)執(zhí)行,可以防止臟讀、不可重復(fù)讀以及幻讀。
使用示例:
@Transactional(isolation = Isolation.READ_COMMITTED) public void performTransaction() { // 業(yè)務(wù)邏輯代碼 }
二、事務(wù)的傳播行為
事務(wù)的傳播行為是指當(dāng)一個(gè)事務(wù)方法被另一個(gè)事務(wù)方法調(diào)用時(shí),兩者之間的事務(wù)應(yīng)該如何關(guān)聯(lián)。通過配置不同的傳播行為,可以控制是否應(yīng)該創(chuàng)建新的事務(wù)、加入現(xiàn)有事務(wù)或者以非事務(wù)方式執(zhí)行等。
Spring 提供了七種標(biāo)準(zhǔn)的事務(wù)傳播行為,它們可以通過 @Transactional 注解的 propagation 屬性來指定。以下是這些傳播行為的詳細(xì)說明:
PROPAGATION_REQUIRED (默認(rèn)):
如果當(dāng)前存在事務(wù),則加入該事務(wù);如果不存在,則創(chuàng)建一個(gè)新的事務(wù)。
這是最常用的傳播行為,適用于大多數(shù)場景。
PROPAGATION_SUPPORTS
如果當(dāng)前存在事務(wù),則加入該事務(wù);如果不存在,則以非事務(wù)方式執(zhí)行。
適合那些對事務(wù)性沒有特別要求的操作,如查詢操作。
PROPAGATION_MANDATORY:
如果當(dāng)前存在事務(wù),則加入該事務(wù);如果不存在,則拋出異常(IllegalTransactionStateException)。
用于強(qiáng)制要求在已有事務(wù)中執(zhí)行的方法。
PROPAGATION_REQUIRES_NEW:
創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則將當(dāng)前事務(wù)掛起。
適用于需要獨(dú)立于外部事務(wù)執(zhí)行的業(yè)務(wù)邏輯,確保內(nèi)部操作不會(huì)影響外部事務(wù)的結(jié)果。
PROPAGATION_NOT_SUPPORTED:
以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),則將當(dāng)前事務(wù)掛起。
適合那些明確不需要事務(wù)的操作,如讀取系統(tǒng)配置或發(fā)送郵件等。
PROPAGATION_NEVER:
以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常(IllegalTransactionStateException)。
用于嚴(yán)格禁止在事務(wù)環(huán)境中執(zhí)行的方法。
PROPAGATION_NESTED:
如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行;如果不存在,則創(chuàng)建一個(gè)新的事務(wù)。
嵌套事務(wù)是外部事務(wù)的一部分,但可以獨(dú)立于外部事務(wù)進(jìn)行提交或回滾。這種傳播行為依賴于底層數(shù)據(jù)庫和驅(qū)動(dòng)的支持,例如 MySQL 的 InnoDB 引擎支持保存點(diǎn)(SAVEPOINT),從而實(shí)現(xiàn)嵌套事務(wù)。
注意事項(xiàng)
性能考慮:選擇合適的傳播行為對于性能優(yōu)化非常重要。例如,PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NOT_SUPPORTED 都會(huì)涉及到事務(wù)的掛起和恢復(fù),這可能會(huì)帶來額外的開銷。
事務(wù)邊界:正確理解事務(wù)的邊界以及傳播行為的影響,有助于避免潛在的問題,如死鎖、數(shù)據(jù)不一致等。
嵌套事務(wù)支持:不是所有的數(shù)據(jù)庫都支持嵌套事務(wù)。使用 PROPAGATION_NESTED 時(shí),請確保你的數(shù)據(jù)庫和驅(qū)動(dòng)程序支持這一特性。
根據(jù)應(yīng)用的具體需求選擇適當(dāng)?shù)膫鞑バ袨椋梢詭椭愀玫毓芾硎聞?wù),確保數(shù)據(jù)的一致性和完整性。
三、Spring事務(wù)中存在的坑
在同一個(gè)類里面,編寫兩個(gè)方法,內(nèi)部調(diào)用的時(shí)候,會(huì)導(dǎo)致事務(wù)設(shè)置失效。原因是沒有用到
代理對象的緣故。具體來說:
Spring 使用 AOP 來實(shí)現(xiàn)事務(wù)管理,它會(huì)為每個(gè)帶有 @Transactional 注解的方法創(chuàng)建一個(gè)代理對象。當(dāng)你通過 Spring 容器獲取這個(gè)類的實(shí)例并調(diào)用其方法時(shí),實(shí)際上是調(diào)用了代理對象的方法,而不是原始類的方法。代理對象負(fù)責(zé)在方法調(diào)用前后插入事務(wù)管理邏輯。
然而,當(dāng)你在一個(gè)類的非靜態(tài)方法中直接調(diào)用另一個(gè) @Transactional 方法時(shí),調(diào)用并沒有經(jīng)過代理對象,而是直接調(diào)用了原始類的方法。因此,事務(wù)管理邏輯不會(huì)被應(yīng)用,導(dǎo)致事務(wù)設(shè)置失效。
方法1:
1)、導(dǎo)入spring-boot-starter-aop依賴
2)、啟動(dòng)類添加注解@EnableAspectJAutoProxy(exposeProxy=true)
3)、事務(wù)使用的地方使用AopContext.currentProxy() 調(diào)用方法。
使用示例:
import org.springframework.aop.framework.AopContext; @Service public class MyService { @Transactional public void transactionalMethod() { // 事務(wù)邏輯 } public void performOperation() { // 業(yè)務(wù)邏輯 ((MyService) AopContext.currentProxy()).transactionalMethod(); } }
不過這種方式使得代碼更加復(fù)雜且不直觀,因此盡量避免使用,除非絕對必要。
最推薦的做法是將事務(wù)方法移到不同的類中。這樣可以確保每次調(diào)用事務(wù)方法時(shí)都通過代理對象進(jìn)行,從而保證事務(wù)管理生效。具體可參考方法2:
方法2:
@Service public class MyService { @Autowired private AnotherService anotherService; public void performOperation() { // 業(yè)務(wù)邏輯 anotherService.transactionalMethod(); } } @Service public class AnotherService { @Transactional public void transactionalMethod() { // 事務(wù)邏輯 } }
拓展:@Transactional支持的配置屬性大盤點(diǎn)
除了上面提到的propagation和isolation,@Transactional 注解里邊還支持配置以下屬性:
1. value 或 transactionManager
- 作用:指定要使用的事務(wù)管理器的名稱。如果你的應(yīng)用程序中有多個(gè)事務(wù)管理器(例如,針對不同的數(shù)據(jù)源),你可以使用這個(gè)屬性來明確指定哪一個(gè)事務(wù)管理器應(yīng)該管理當(dāng)前方法的事務(wù)。
- 默認(rèn)值:
"transactionManager"
,這是 Spring 默認(rèn)的事務(wù)管理器 Bean 名稱。
@Transactional("myTransactionManager") public void myTransactionalMethod() { // 業(yè)務(wù)邏輯 }
2. readOnly 作用:
指定事務(wù)是否為只讀事務(wù)。只讀事務(wù)通常用于查詢操作,可以提高性能(例如,禁用臟頁寫入等)。
取值:
false
(默認(rèn)):事務(wù)不是只讀的,允許進(jìn)行插入、更新和刪除操作。true
:事務(wù)是只讀的,僅允許查詢操作。
@Transactional(readOnly = true) public List<Entity> findAllEntities() { // 查詢操作 }
3. timeout
作用:定義事務(wù)的超時(shí)時(shí)間(單位為秒)。如果事務(wù)在指定時(shí)間內(nèi)未能完成,Spring 會(huì)自動(dòng)回滾事務(wù)。
默認(rèn)值:-1,表示使用后端數(shù)據(jù)庫或事務(wù)管理器的默認(rèn)超時(shí)設(shè)置。
@Transactional(timeout = 30) public void longRunningOperation() { // 長時(shí)間運(yùn)行的業(yè)務(wù)邏輯 }
4. rollbackFor
作用:指定哪些異常應(yīng)該觸發(fā)事務(wù)回滾。默認(rèn)情況下,只有未檢查異常(如 RuntimeException 及其子類)會(huì)觸發(fā)回滾。你可以通過這個(gè)屬性指定其他異常類型也應(yīng)觸發(fā)回滾。
取值:一個(gè)或多個(gè)異常類,可以用逗號(hào)分隔。
@Transactional(rollbackFor = {CustomCheckedException.class, AnotherException.class}) public void methodThatMayThrowExceptions() { // 業(yè)務(wù)邏輯 }
5. noRollbackFor
作用:指定哪些異常不應(yīng)該觸發(fā)事務(wù)回滾。默認(rèn)情況下,所有未檢查異常都會(huì)觸發(fā)回滾,但你可以通過這個(gè)屬性指定某些異常不應(yīng)觸發(fā)回滾。
取值:一個(gè)或多個(gè)異常類,可以用逗號(hào)分隔。
@Transactional(noRollbackFor = CustomCheckedException.class) public void methodThatMayThrowCustomException() { // 業(yè)務(wù)邏輯 }
6. validation
作用:指定是否在事務(wù)開始之前驗(yàn)證事務(wù)屬性。如果設(shè)置為 true,Spring 會(huì)在事務(wù)開始前檢查事務(wù)屬性是否符合要求,如果不符則拋出異常。
默認(rèn)值:false,即不進(jìn)行驗(yàn)證。
@Transactional(validation = true) public void validateTransactionalAttributes() { // 業(yè)務(wù)邏輯 }
到此這篇關(guān)于Springboot @Transactional使用時(shí)需注意的幾個(gè)問題的文章就介紹到這了,更多相關(guān)Springboot @Transactional使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring中@PropertySource的使用方法和運(yùn)行原理詳解
這篇文章主要介紹了Spring中@PropertySource的使用方法和運(yùn)行原理詳解,PropertySource注解可以方便和靈活的向Spring的環(huán)境容器(org.springframework.core.env.Environment?Environment)中注入一些屬性,這些屬性可以在Bean中使用,需要的朋友可以參考下2023-11-11springboot2.x解決運(yùn)行順序及Bean對象注入順序的問題
這篇文章主要介紹了springboot2.x解決運(yùn)行順序及Bean對象注入順序的問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01MybatisPlus+Postgresql整合的幾個(gè)坑及解決
這篇文章主要介紹了MybatisPlus+Postgresql整合的幾個(gè)坑及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03Spring?AOP?的實(shí)現(xiàn)和切點(diǎn)表達(dá)式的實(shí)現(xiàn)方式
本文給大家介紹了Spring?AOP的基本概念、通知類型、切點(diǎn)表達(dá)式和切面優(yōu)先級(jí),并通過示例代碼展示了如何實(shí)現(xiàn)這些功能,感興趣的朋友跟隨小編一起看看吧2024-12-12