Spring事务传播:特定事务回滚需求及代码问题排查
Spring @Transactional(REQUIRES_NEW) 不生效问题排查与解决
问题场景
调用studentService.getStudent("ram")时,期望secondChange()和thirdChange()中的修改因异常回滚,仅保留getStudent()方法的"first change done"修改,但实际数据库中SUBJECT字段为"third change done",事务配置未生效。
核心原因
- 同类方法调用导致事务代理失效:Spring声明式事务依赖动态代理实现,同一个类内部直接调用带
@Transactional注解的方法时,不会经过代理对象,因此secondChange上的Propagation.REQUIRES_NEW配置完全不生效,所有数据库操作都处于getStudent方法的同一个事务中。 - 异常被捕获导致事务未回滚:虽然
secondChange抛出了RuntimeException,但该异常被getStudent的try-catch块捕获,Spring事务默认仅对未被捕获的RuntimeException触发回滚,因此整个事务最终正常提交,thirdChange中的修改也被持久化到数据库。
解决方案
方案1:将事务方法抽离到独立Service类
把secondChange方法移到新的Service类中,通过依赖注入调用,确保走代理对象触发事务逻辑:
@Service public class StudentTransactionService { @Autowired private StudentRepo studentRepo; @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) public void secondChange(String name) { Student student = studentRepo.findByName(name); student.setSubject("second change done"); thirdChange(name); throw new RuntimeException(); } private void thirdChange(String name) { Student student = studentRepo.findByName(name); student.setSubject("third change done"); } }
修改原StudentService:
@Service public class StudentService { @Autowired private StudentRepo studentRepo; @Autowired private StudentTransactionService transactionService; @Transactional public Student getStudent(String name){ Student student = studentRepo.findByName(name); student.setSubject("first change done"); studentRepo.save(student); try{ transactionService.secondChange(name); } catch (Exception e){ System.out.println("***********caught"); } return student; } }
方案2:使用AopContext获取代理对象调用方法
如果不想拆分类,可通过AopContext获取当前代理对象调用方法,但需先开启暴露代理配置:
- 在启动类/配置类添加注解:
@SpringBootApplication @EnableAspectJAutoProxy(exposeProxy = true) public class TransactionDemoApplication { // ... 其他代码 }
- 修改
StudentService的调用逻辑:
@Transactional public Student getStudent(String name){ Student student = studentRepo.findByName(name); student.setSubject("first change done"); studentRepo.save(student); try{ // 通过代理对象调用,触发REQUIRES_NEW事务 ((StudentService) AopContext.currentProxy()).secondChange(name); } catch (Exception e){ System.out.println("***********caught"); } return student; }
补充说明
- 当
REQUIRES_NEW事务生效后,secondChange会开启独立新事务,抛出异常后新事务回滚,不会影响getStudent的原有事务,最终数据库仅保留"first change done"的修改。 - JPA中,处于持久化上下文的实体对象被修改后,事务提交时会自动同步到数据库,无需手动调用
save方法(调用也不会产生问题)。
内容的提问来源于stack exchange,提问作者Amit Chaudhary
相关产品推荐
相关产品推荐

