在同一個 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 代理實現的。當你在同一個 Bean 內部通過 this.updateDep(emp) 直接調用方法時,繞過了代理對象,導致 @Transactional 註解上的傳播行為配置不生效。實際執行的是目標對象的原始方法,並非代理增強後的方法。
將 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 中注入自己(需配合 @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 進行編譯時或加載時織入,不依賴代理,內部調用也能生效。但會增加構建複雜度,通常不建議僅為此引入。
Spring 默認僅對未檢查異常(RuntimeException 及其子類、Error)觸發回滾。你的代碼拋出的是 Exception(受檢異常),若未在 @Transactional(rollbackFor = Exception.class) 中聲明,外層事務其實不會回滾。但這不影響 REQUIRES_NEW 失效的核心原因。
REQUIRES_NEW 方法,新事務不生效,隨外層回滾。this.method() 繞過 Spring 代理,事務註解失效。理解 Spring 事務代理機制,是避開此類坑的關鍵。