首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Spring 事務嵌套:Propagation.REQUIRES_NEW 為何未生效?

     編輯:程式設計綜合問答

問題場景

在同一個 Spring Bean 中,外層方法使用 @Transactional(propagation=Propagation.REQUIRED),內層方法使用 @Transactional(propagation=Propagation.REQUIRES_NEW)。代碼如下:

@Transactional(propagation = Propagation.REQUIRED)
public void insertEmploye(Employe emp) {
    this.getSqlSession().insert("insert", emp);
    this.updateDep(emp); // 內部調用
    throw new Exception("My Exception");
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateDep(Employe emp) {
    this.getSqlSession().update("updateCount", emp);
}

拋出異常後,外層事務回滾,但內層 updateDep 的更新並未提交,updatedCount 沒有更新。按理說 REQUIRES_NEW 應開啟獨立事務,隔離外層回滾,為何失效?

核心原因:Spring AOP 代理的自我調用限制

Spring 的事務管理是基於 AOP 代理實現的。當你在同一個 Bean 內部通過 this.updateDep(emp) 直接調用方法時,繞過了代理對象,導致 @Transactional 註解上的傳播行為配置不生效。實際執行的是目標對象的原始方法,並非代理增強後的方法。

解決方案

方案一:將內層方法移至另一個 Bean(推薦)

將 updateDep 遷移到獨立的 Service 類中,並注入到當前 Bean。這樣調用時會走代理,REQUIRES_NEW 才會真正開啟新事務。

@Service
public class DepService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateDep(Employe emp) {
        this.getSqlSession().update("updateCount", emp);
    }
}

@Service
public class EmployeeService {
    @Autowired
    private DepService depService;

    @Transactional(propagation = Propagation.REQUIRED)
    public void insertEmploye(Employe emp) {
        this.getSqlSession().insert("insert", emp);
        depService.updateDep(emp); // 走代理調用
        throw new Exception("My Exception");
    }
}

方案二:注入自身 Bean 並通過代理調用(不推薦)

在當前 Bean 中注入自己(需配合 @Lazy 避免循環依賴),調用時使用注入的引用:

@Autowired @Lazy
private EmployeeService self;

@Transactional(propagation = Propagation.REQUIRED)
public void insertEmploye(Employe emp) {
    this.getSqlSession().insert("insert", emp);
    self.updateDep(emp); // 通過代理調用
    throw new Exception("My Exception");
}

方案三:使用 AspectJ 編譯時織入(較少用)

改用 AspectJ 進行編譯時或加載時織入,不依賴代理,內部調用也能生效。但會增加構建複雜度,通常不建議僅為此引入。

補充說明:默認回滾規則

Spring 默認僅對未檢查異常(RuntimeException 及其子類、Error)觸發回滾。你的代碼拋出的是 Exception(受檢異常),若未在 @Transactional(rollbackFor = Exception.class) 中聲明,外層事務其實不會回滾。但這不影響 REQUIRES_NEW 失效的核心原因。

總結

  • 現象:同類內部調用 REQUIRES_NEW 方法,新事務不生效,隨外層回滾。
  • 本質:this.method() 繞過 Spring 代理,事務註解失效。
  • 最佳實踐:將需要不同傳播行為的方法拆分到不同的 Bean 中,通過依賴注入調用。

理解 Spring 事務代理機制,是避開此類坑的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved