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

Spring Boot中Propagation.REQUIRED事务异常未回滚问题求助

问题根因

Spring 声明式事务基于 AOP 动态代理实现,只有事务方法被代理对象调用时,@Transactional 注解才会生效。你当前代码存在的核心问题是同类内部方法自调用:

  • 入口run方法直接调用本类的insertUserBunty,没有走代理对象,该方法上的@Transactional注解完全不生效,没有开启全局事务
  • 后续insertUserBunty调用本类的insertUserKumar同样是本地方法调用,该方法的事务注解也不会生效

相当于两个save操作都运行在无事务环境下,每个持久化操作执行后就自动提交,异常抛出时两个写入操作已经完成提交,自然不会回滚,这也是你加/不加@EnableTransactionManagement都不生效的根本原因。

解决方案

下面是三种常用的修复方式,按需选择即可:

方案1:拆分事务方法到独立Bean

将insertUserBunty、insertUserKumar抽到单独的事务Bean中,在AppRunner中注入该Bean调用方法,调用链路走代理即可触发事务切面逻辑。
示例代码:

// 单独抽离的事务服务类
@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;

    @Transactional(propagation = Propagation.REQUIRED)
    public void insertUserBunty() {
        User bunty = new User(null, "Bunty");
        userRepository.save(bunty);
        insertUserKumar();
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void insertUserKumar() {
        User kumar = new User(null, "Kumar");
        userRepository.save(kumar);
        throw new RuntimeException("This will rollback both insert Bunty and Kumar");
    }
}

// 修改AppRunner
@Slf4j
@Component
@RequiredArgsConstructor
public class AppRunner implements CommandLineRunner {
    private final UserService userService;

    @Override
    public void run(String... args) throws Exception {
        userService.insertUserBunty();
    }
}

方案2:注入自身代理对象

在当前类中注入自身的代理对象,通过代理对象调用事务方法,注意添加@Lazy注解避免循环依赖问题,同时需要把事务方法改为public权限:

@Slf4j
@Component
@RequiredArgsConstructor
public class AppRunner implements CommandLineRunner {
    private final UserRepository userRepository;
    @Lazy
    @Autowired
    private AppRunner self;

    @Override
    public void run(String... args) throws Exception {
        self.insertUserBunty();
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void insertUserBunty() {
        User bunty = new User(null, "Bunty");
        userRepository.save(bunty);
        self.insertUserKumar();
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void insertUserKumar() {
        User kumar = new User(null, "Kumar");
        userRepository.save(kumar);
        throw new RuntimeException("This will rollback both insert Bunty and Kumar");
    }
}

方案3:使用编程式事务

直接使用TransactionTemplate手动控制事务边界,无需依赖代理机制:

@Slf4j
@Component
@RequiredArgsConstructor
public class AppRunner implements CommandLineRunner {
    private final UserRepository userRepository;
    private final TransactionTemplate transactionTemplate;

    @Override
    public void run(String... args) throws Exception {
        transactionTemplate.execute(status -> {
            User bunty = new User(null, "Bunty");
            userRepository.save(bunty);
            User kumar = new User(null, "Kumar");
            userRepository.save(kumar);
            throw new RuntimeException("This will rollback both insert Bunty and Kumar");
        });
    }
}
额外注意事项
  • 事务方法尽量设置为public权限,非public方法在部分代理模式下会导致注解失效
  • 不要在事务方法内部捕获异常不抛出,事务默认只会在遇到未捕获的RuntimeException和Error时回滚,如果需要捕获异常请手动设置回滚标记,或者修改rollbackFor属性指定回滚异常类型

内容的提问来源于stack exchange,提问作者Bal Vikash Sharma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 10:15:00