You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring事务传播:特定事务回滚需求及代码问题排查

Spring @Transactional(REQUIRES_NEW) 不生效问题排查与解决

问题场景

调用studentService.getStudent("ram")时,期望secondChange()和thirdChange()中的修改因异常回滚,仅保留getStudent()方法的"first change done"修改,但实际数据库中SUBJECT字段为"third change done",事务配置未生效。

核心原因

  1. 同类方法调用导致事务代理失效:Spring声明式事务依赖动态代理实现,同一个类内部直接调用带@Transactional注解的方法时,不会经过代理对象,因此secondChange上的Propagation.REQUIRES_NEW配置完全不生效,所有数据库操作都处于getStudent方法的同一个事务中。
  2. 异常被捕获导致事务未回滚:虽然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获取当前代理对象调用方法,但需先开启暴露代理配置:

  1. 在启动类/配置类添加注解:
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
public class TransactionDemoApplication {
    // ... 其他代码
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 23:24:54