先分析一下
1,內部調用,@原對象調用,不是代理對象調用
2,private方法,@源碼中,只能是public方法
3,異常被捕獲了,@事物攔截器,無法感知
4,子線程調用,@事物TreadLocal實現,主子線程獨立
5,Roback 設置不對,@默認對RuntimeExcpetion+error有效,sql,io無效,@Exception
6,傳播機制不對,@被調用方法B是new,A調用B,A異常,B不會滾,他是新的事物
7,存儲引擎不是innodb
8,事物未開啟
DS
@Transactional
?注解失效的場景及原因分析
@Transactional
?是 Spring 聲明式事務的核心注解,但在某些場景下會失效。以下是常見的失效場景及其根本原因:
1. 自調用(同一個類中方法調用)
場景:
java
復制
下載
public class OrderService {public void createOrder() {this.updateStatus(); // 自調用,事務失效}@Transactionalpublic void updateStatus() {// 業務邏輯} }
原因:
-
Spring 事務基于 AOP 代理實現,自調用時通過?
this
?調用的原始對象而非代理對象,導致攔截器無法介入。
解決方案:
-
注入自身代理:
@Autowired private OrderService self;
?然后調用?self.updateStatus()
-
將方法拆分到不同類中
2. 方法訪問權限非 public
場景:
java
復制
下載
@Transactional private void updateData() { // 事務失效// ... }
原因:
-
Spring 默認只代理 public 方法(CGLIB 可代理 protected,但官方不建議)。
-
動態代理(JDK/CGLIB)無法攔截 private 方法。
解決方案:
-
將方法改為?
public
-
改用編程式事務(
TransactionTemplate
)
3. 異常類型不匹配
場景:
java
復制
下載
@Transactional public void process() throws Exception {try {// 可能拋出IOException} catch (IOException e) {throw new Exception("業務異常"); // 非RuntimeException} }
原因:
-
默認只對?
RuntimeException
?和?Error
?回滾,受檢異常(如?Exception
)不會觸發回滾。
解決方案:
-
明確指定回滾異常:
java
復制
下載
@Transactional(rollbackFor = Exception.class)
-
拋出?
RuntimeException
?或其子類
4. 數據庫引擎不支持事務
場景:
-
使用 MyISAM 引擎的 MySQL 表
-
某些 NoSQL 數據庫(如 MongoDB 舊版本)
原因:
-
MyISAM 不支持事務,僅 InnoDB 支持。
-
部分數據庫無事務能力。
解決方案:
-
切換為 InnoDB 引擎:
sql
復制
下載
ALTER TABLE your_table ENGINE=InnoDB;
5. 多線程調用
場景:
java
復制
下載
@Transactional public void asyncTask() {new Thread(() -> {// 子線程中的操作不受事務控制}).start(); }
原因:
-
事務信息通過?
ThreadLocal
?存儲,子線程無法繼承父線程的事務上下文。
解決方案:
-
使用?
TransactionTemplate
?編程式事務 -
通過異步事件(如?
@TransactionalEventListener
)處理
6. 未啟用事務管理
場景:
-
忘記添加?
@EnableTransactionManagement
-
配置錯誤(如錯誤的?
PlatformTransactionManager
?Bean)
原因:
-
Spring 未掃描到?
@Transactional
?注解或未創建代理。
解決方案:
-
確保配置類添加注解:
java
復制
下載
@Configuration @EnableTransactionManagement // 關鍵 public class AppConfig { ... }
-
檢查事務管理器 Bean 是否正確注入
7. 嵌套事務傳播行為不當
場景:
java
復制
下載
@Transactional(propagation = Propagation.REQUIRED) public void methodA() {methodB(); }@Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() {// 如果methodA拋異常,methodB仍會提交 }
原因:
-
REQUIRES_NEW
?會掛起當前事務,獨立提交。若外層事務回滾,內層事務不受影響。
解決方案:
-
根據業務需求調整傳播行為(如改用?
NESTED
)
8. 事務方法內捕獲異常未重新拋出
場景:
java
復制
下載
@Transactional public void process() {try {// 可能拋出異常的操作} catch (Exception e) {log.error("錯誤", e); // 僅記錄不拋出} }
原因:
-
攔截器無法感知異常,事務標記為成功。
解決方案:
-
捕獲后手動回滾:
java
復制
下載
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
-
或重新拋出異常
總結:事務失效排查清單
失效場景 | 關鍵特征 | 驗證方法 |
---|---|---|
自調用 | 同類中非代理調用 | 檢查是否通過?this ?調用 |
非 public 方法 | 方法為 private/protected | 查看方法修飾符 |
異常類型不匹配 | 捕獲后未拋出或拋出受檢異常 | 檢查異常處理邏輯 |
數據庫引擎不支持 | 使用 MyISAM 引擎 | SHOW TABLE STATUS ?查看引擎 |
多線程調用 | 子線程內操作 | 檢查是否有?new Thread() |
未啟用事務管理 | 無事務管理器 Bean | 檢查?@EnableTransactionManagement |
嵌套事務傳播不當 | 內外層事務相互影響 | 檢查?propagation ?配置 |
捕獲異常未處理 | try-catch 吞沒異常 | 檢查日志是否有未拋出的異常記錄 |
終極調試技巧:
在調試模式下查看目標對象類名:
-
代理對象:
com.sun.proxy.$ProxyXX
(JDK)或?UserService$$EnhancerBySpringCGLIB
-
原始對象:
com.example.UserService
若調用的是原始對象,則事務必然失效。