Spring Boot中@Transactional注解抛出异常时未回滚JPA保存操作问题排查
看起来你遇到的核心问题是事务边界设置错误,导致数据库操作提前提交,后续抛出异常时无法触发回滚。我来帮你一步步排查和解决:
问题根源
你的finishWalletTransaction方法上标注了@Transactional,这意味着这个方法执行时会开启一个独立事务。当该方法执行完毕(数据库保存操作完成),这个事务就会自动提交。后续在doWalletOperation中抛出的异常,已经和之前提交的事务无关,自然无法回滚已经写入数据库的记录。
另外补充一点:doWalletOperation是private方法,即使你给它加@Transactional也不会生效——Spring AOP代理只能拦截public方法的调用。
具体解决方案
1. 调整事务边界到业务入口
把事务注解移到整个业务流程的入口方法(也就是doWalletOperation所在的类),确保整个操作处于同一个事务中:
@Service public class YourBusinessService { // 假设这是doWalletOperation所在的业务类 @Autowired private CommonTransactionalService commonTransactionalService; // 其他依赖注入... // 把事务注解加到public的业务入口方法上 @Transactional(rollbackFor = {RuntimeException.class, WalletException.class}) public BtoBWalletTransactionResponseModel doWalletOperation(BtoBWalletTransactionTypes transactionType, BtoBWalletTransactionRequestModel transactionRequest) { try { // 省略获取userWallet、btoBWalletTransaction等前置逻辑 BtoBWalletTransaction savedTransaction = commonTransactionalService.finishWalletTransaction(userWallet, btoBWalletTransaction); log.info("wallet {} txn of amount {} for user {}",transactionType.name(),txnAmount,userId); // 抛出异常以触发回滚 throw new RuntimeException("Time to Rollback"); } catch(Exception e){ log.error(e.getMessage()); log.error("error while doing wallet operations for user {}",userId); throw new WalletException(e.getMessage()); } } }
2. 移除下层方法的事务注解
因为现在事务由上层业务入口统一管理,所以需要删除CommonTransactionalServiceImpl中finishWalletTransaction方法上的@Transactional:
@Service public class CommonTransactionalServiceImpl implements CommonTransactionalService { @Autowired private BtoBWalletTransactionRepo btoBWalletTransactionRepo; @Autowired private BtoBUserWalletRepo btoBUserWalletRepo; // 补充你代码中遗漏的注入 @Override public BtoBWalletTransaction finishWalletTransaction(BtoBUserWallet userWallet, BtoBWalletTransaction walletTransaction) { BtoBWalletTransaction savedTransaction = btoBWalletTransactionRepo.save(walletTransaction); btoBUserWalletRepo.save(userWallet); return savedTransaction; } }
3. 确认异常回滚配置
注意:如果你的WalletException是自定义的checked异常(继承自Exception而非RuntimeException),默认@Transactional不会对其触发回滚,所以需要在注解中显式指定回滚的异常类型,就像上面代码中写的rollbackFor = {RuntimeException.class, WalletException.class}。
4. 验证Spring事务基础配置
确保你的Spring Boot应用已经正确开启事务管理:
- 启动类可以添加
@EnableTransactionManagement注解(Spring Boot默认会自动配置,但显式添加更稳妥) - 所有涉及事务的类都是通过
@Autowired注入的Spring Bean,而非手动new的对象(手动实例化的对象不会被Spring代理,事务注解无效)
测试验证
调整完成后,重新运行代码:当doWalletOperation抛出RuntimeException时,整个事务会被回滚,finishWalletTransaction中的两次数据库保存操作都会被撤销。
内容的提问来源于stack exchange,提问作者Himanshu Ranjan

