SpringBoot中事務(wù)失效的六個原因解析
SpringBoot中事務(wù)失效的原因
常見的事務(wù)失效原因包括如下六個:
1. 事務(wù)方法非public修飾
由于Spring的事務(wù)是基于AOP的方式結(jié)合動態(tài)代理來實(shí)現(xiàn)的。因此事務(wù)方法一定要是public的,這樣才能便于被Spring做事務(wù)的代理和增強(qiáng)。
而且,在Spring內(nèi)部也會有一個 org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource類,去檢查事務(wù)方法的修飾符:
@Nullable
protected TransactionAttribute computeTransactionAttribute(
Method method, @Nullable Class<?> targetClass) {
// Don't allow non-public methods, as configured.
if (allowPublicMethodsOnly() &&
!Modifier.isPublic(method.getModifiers())) {
return null;
}
// ... 略
return null;
}
2. 非事務(wù)方法調(diào)用事務(wù)方法
@Service
public class OrderService {
public void createOrder(){
// ... 準(zhǔn)備訂單數(shù)據(jù)
// 生成訂單并扣減庫存
insertOrderAndReduceStock();
}
@Transactional
public void insertOrderAndReduceStock(){
// 生成訂單
insertOrder();
// 扣減庫存
reduceStock();
}
}
可以看到,insertOrderAndReduceStock方法是一個事務(wù)方法,肯定會被Spring事務(wù)管理。Spring會給OrderService類生成一個動態(tài)代理對象,對insertOrderAndReduceStock方法做增加,實(shí)現(xiàn)事務(wù)效果。
但是現(xiàn)在createOrder方法是一個非事務(wù)方法,在其中調(diào)用了insertOrderAndReduceStock方法,這個調(diào)用其實(shí)隱含了一個this.的前綴。也就是說,這里相當(dāng)于是直接調(diào)用原始的OrderService中的普通方法,而非被Spring代理對象的代理方法。那事務(wù)肯定就失效了!
3. 事務(wù)方法的異常被捕獲了
異常被捕獲了但是沒有往外拋異常,所以事務(wù)沒有發(fā)現(xiàn)方法中出現(xiàn)錯誤,所以也就沒有回滾
@Transactional
public void createOrder(){
// ... 準(zhǔn)備訂單數(shù)據(jù)
// 生成訂單
insertOrder();
// 扣減庫存
reduceStock();
}
private void reduceStock() {
try {
// ...扣庫存
} catch (Exception e) {
// 處理異常
}
}
在這段代碼中,reduceStock方法內(nèi)部直接捕獲了Exception類型的異常,也就是說方法執(zhí)行過程中即便出現(xiàn)了異常也不會向外拋出。
而Spring的事務(wù)管理就是要感知業(yè)務(wù)方法的異常,當(dāng)捕獲到異常后才會回滾事務(wù)。
現(xiàn)在事務(wù)被捕獲,就會導(dǎo)致Spring無法感知事務(wù)異常,自然不會回滾,事務(wù)就失效了。
4. 事務(wù)異常類型不對
@Transactional(rollbackFor = RuntimeException.class)
public void createOrder() throws IOException {
// ... 準(zhǔn)備訂單數(shù)據(jù)
// 生成訂單
insertOrder();
// 扣減庫存
reduceStock();
throw new IOException();
}

Spring的事務(wù)管理默認(rèn)感知的異常類型是RuntimeException,當(dāng)事務(wù)方法內(nèi)部拋出了一個IOException時,不會被Spring捕獲,因此就不會觸發(fā)事務(wù)回滾,事務(wù)就失效了。
因此,當(dāng)我們的業(yè)務(wù)中會拋出RuntimeException以外的異常時,應(yīng)該通過@Transactional注解中的rollbackFor屬性來指定異常類型:
@Transactional(rollbackFor = Exception.class)
5.事務(wù)傳播行為不對
@Transactional
public void createOrder(){
// 生成訂單
insertOrder();
// 扣減庫存
reduceStock();
throw new RuntimeException("業(yè)務(wù)異常");
}
@Transactional // 默認(rèn)的是如果當(dāng)前沒有事務(wù),自己創(chuàng)建事務(wù),如果有事務(wù)則加入
public void insertOrder() {
}
// 不管當(dāng)前方法所在方法有沒有都開啟一個事務(wù)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void reduceStock() {
}
在示例代碼中,事務(wù)的入口是createOrder()方法,會開啟一個事務(wù),可以成為外部事務(wù)。在createOrder()方法內(nèi)部又調(diào)用了insertOrder()方法和reduceStock()方法。這兩個都是事務(wù)方法。
不過,reduceStock()方法的事務(wù)傳播行為是REQUIRES_NEW,這會導(dǎo)致在進(jìn)入reduceStock()方法時會創(chuàng)建一個新的事務(wù),可以成為子事務(wù)。insertOrder()則是默認(rèn),因此會與createOrder()合并事務(wù)。
因此,當(dāng)createOrder方法最后拋出異常時,只會導(dǎo)致insertOrder方法回滾,而不會導(dǎo)致reduceStock方法回滾,因?yàn)閞educeStock是一個獨(dú)立事務(wù)。
所以,一定要慎用傳播行為,注意外部事務(wù)與內(nèi)部事務(wù)之間的關(guān)系。
6.沒有被Spring管理
即當(dāng)前類沒有被SpringBoot掃描
第二種事務(wù)失效的解決方案:
上面的問題在于非事務(wù)方法中調(diào)用事務(wù)方法其中隱含了一個this.的前綴, 雖然當(dāng)前方法的事務(wù)也被代理類生成了,但是因?yàn)槟J(rèn)關(guān)鍵字的原因,調(diào)用的還是原來的是沒有事務(wù)的方法.
所以我們現(xiàn)在要做的就是要找到被代理之后的類,然后再在方法中調(diào)用該方法
1)引入AspectJ依賴:
<!--aspecj-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
2)暴露代理對象
在啟動類上添加注解,暴露代理對象:

3)使用代理對象
通過AopContext拿到當(dāng)前類的代理對象,然后調(diào)用對應(yīng)方法
// 返回值是Object IUserCouponService userCouponService = (IUserCouponService) AopContext.currentProxy(); userCouponService.insertCouponAndCheck(userId, coupon, null);
(補(bǔ)充 )StringBoot中事務(wù)相關(guān)的接口
在Spring中有兩個和事務(wù)相關(guān)的接口
PlatformTransactionManager 平臺事務(wù)管理接口
作用; 對于不同的數(shù)據(jù)源采用不同的管理平臺, 常用的實(shí)現(xiàn)類如下
- DataSourceTransactionManager:使用JDBC或MyBatis進(jìn)行事務(wù)管理。適用于DataSource數(shù)據(jù)源。
- HibernateTransactionManager:使用Hibernate進(jìn)行事務(wù)管理。適用于Hibernate持久層框架。
TransactionDefinition 事務(wù)定義接口
TransactionDefinition 接口中定義了事務(wù)的描述相關(guān)的三類常量:
- 事務(wù)隔離級別
- 事務(wù)傳播行為
- 事務(wù)默認(rèn)超時時限

1. 事務(wù)隔離級別
- DEFAULT : 采用DB默認(rèn)的事務(wù)隔離級別.mysql中默認(rèn)的是 REPEATABLE_READ (repeatable_read)
- READ_UNCOMMITTED: 讀未提交 未解決任何并發(fā)問題
- READ_COMMITTED: 讀已提交 解決了臟讀,存在不可重復(fù)讀和幻讀
- REPETABLE_READ : 可重復(fù)讀,解決了臟讀,不可重復(fù)讀,存在幻讀
- SERIALIZABLE :串行化.不存在并發(fā)問題
那么什么是臟讀, 幻讀和 不可重復(fù)讀呢?
臟讀(dirty read): 當(dāng)一個事務(wù)讀取另一個事務(wù)尚未提交的修改時,產(chǎn)生臟讀
不可重復(fù)讀(nonrepeatable read):同一查詢在同一事務(wù)中多次進(jìn)行,由于其他提交事務(wù)所做的修改或刪除,每次返回不同的結(jié)果集,此時發(fā)生不可重復(fù)讀
幻讀(phantom read):同一查詢在同一事務(wù)中多次進(jìn)行,由于其他提交事務(wù)所做的插入操作,每次返回不同的結(jié)果集,此時發(fā)生幻讀
2. 事務(wù)的傳播行為
定義了七個事務(wù)的傳播行為:都是以 PROPAGATION_開頭 propagation(常用三個)
事務(wù)傳播行為是指,處于不同事務(wù)中的方法在相互調(diào)用時,執(zhí)行期間事務(wù)的維護(hù)情況
- propagation_required (spring默認(rèn)的傳播行為)
- propagation_requires_new
- propagation_supports
propagation_required :
說明:指定的方法必須在事務(wù)內(nèi)執(zhí)行。若當(dāng)前存在事務(wù),就加入到當(dāng)前事務(wù)中;若當(dāng)前沒有事務(wù),則創(chuàng)建一個新事務(wù)。這種傳播行為是最常見的選擇,也是 Spring 默認(rèn)的事務(wù)傳播行為。
演示說明:
**如該傳播行為加在doOther()**方法上。若 doSome()方法在調(diào)用 doOther()方法時就是在事務(wù)內(nèi)運(yùn)行的,則 doOther()方法的執(zhí)行也加入到該事務(wù)內(nèi)執(zhí)行。若 doSome()方法在調(diào)用 doOther()方法時沒有在事務(wù)內(nèi)執(zhí)行,則 doOther()方法會創(chuàng)建一個事務(wù),并在其中執(zhí)行。

propagation_requires_new:
說明:總是新建一個事務(wù),如當(dāng)前存在事務(wù),就將當(dāng)前事務(wù)掛起,直到新事務(wù)執(zhí)行完畢

propagation_supports:
說明:指定的方法支持當(dāng)前事務(wù),但若當(dāng)前沒有事務(wù),也可以以非事務(wù)方法執(zhí)行

3. 事務(wù)超時時限
該值一般就是用默認(rèn)值
到此這篇關(guān)于SpringBoot中事務(wù)失效的六個原因解析的文章就介紹到這了,更多相關(guān)SpringBoot事務(wù)失效原因內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot實(shí)現(xiàn)返回視圖而不是string的方法
這篇文章主要介紹了springboot實(shí)現(xiàn)返回視圖而不是string的方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
Springboot 整合 Java DL4J 實(shí)現(xiàn)智能客服功能
本文主要介紹了如何使用SpringBoot整合JavaDeeplearning4j來構(gòu)建一個智能客服系統(tǒng),詳細(xì)探討了神經(jīng)網(wǎng)絡(luò)選擇、數(shù)據(jù)集格式、技術(shù)介紹、Maven依賴、代碼示例等內(nèi)容,為構(gòu)建高效、便捷、個性化的客戶服務(wù)提供了理論支持和實(shí)踐指導(dǎo)2024-10-10
SpringBoot整合WebSocket實(shí)現(xiàn)后端向前端主動推送消息方式
這篇文章主要介紹了SpringBoot整合WebSocket實(shí)現(xiàn)后端向前端主動推送消息方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10
Java利用InputStream類實(shí)現(xiàn)文件讀取與處理
在Java開發(fā)中,輸入流(InputStream)是一個非常重要的概念,它涉及到文件讀寫、網(wǎng)絡(luò)傳輸?shù)榷鄠€方面,InputStream類是Java中輸入流的抽象基類,定義了讀取輸入流數(shù)據(jù)的方法,本文將以InputStream類為切入點(diǎn),介紹Java中的輸入流概念及其應(yīng)用,需要的朋友可以參考下2023-11-11
Java?設(shè)計(jì)模式以虹貓藍(lán)兔的故事講解簡單工廠模式
簡單工廠模式是屬于創(chuàng)建型模式,又叫做靜態(tài)工廠方法(Static Factory Method)模式,但不屬于23種GOF設(shè)計(jì)模式之一。簡單工廠模式是由一個工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的實(shí)例。簡單工廠模式是工廠模式家族中最簡單實(shí)用的模式,可以理解為是不同工廠模式的一個特殊實(shí)現(xiàn)2022-03-03

