RestClientException抛出后Spring事务未完全回滚问题排查求助
排查Spring事务未回滚:Rest调用失败时数据库操作未回滚的问题
核心问题分析
我一眼就看到了问题所在——你的事务没回滚的核心原因是异常被你自己捕获但没有重新抛出,导致Spring的事务管理器完全没察觉到异常发生!
Spring的@Transactional是基于AOP实现的,事务管理器只会在异常从被代理的事务方法(也就是myMethod)抛出时,才会触发回滚逻辑。现在你把异常“吞”在了方法内部,只做了e.printStackTrace()就没下文了,事务管理器根本不知道之前的数据库操作需要回滚。
修复方案
这里给你两种可行的修复方式,根据你的业务需求选择:
方案1:移除catch块,让异常自然传播
如果不需要在方法内部做额外的异常处理,直接移除catch块,让异常自然抛出给事务管理器:
@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { Throwable.class }) @Override public MyClass myMethod(myParams) { myRepo.saveAll(myEntityList); ResponseEntity<String> responseEntity = restTemplate.postForEntity(myUrl, requestParam, String.class); if (responseEntity.getBody() != null && StringUtils.equalsIgnoreCase(responseEntity.getBody(), "Failure")) { log.error("Rest Call Failed"); throw new RestClientException("Error in remote service"); } // 记得返回对应的MyClass实例,原代码缺失了返回逻辑 return new MyClass(...); }
方案2:保留catch块,但重新抛出异常
如果需要在方法内部做一些日志记录或前置处理,一定要在catch块里重新抛出异常:
@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { Throwable.class }) @Override public MyClass myMethod(myParams) { try { myRepo.saveAll(myEntityList); ResponseEntity<String> responseEntity = restTemplate.postForEntity(myUrl, requestParam, String.class); if (responseEntity.getBody() != null && StringUtils.equalsIgnoreCase(responseEntity.getBody(), "Failure")) { log.error("Rest Call Failed"); throw new RestClientException("Error in remote service"); } } catch (Exception e) { // 建议用日志框架打印异常,比printStackTrace更规范 log.error("Exception occurred during processing", e); // 重新抛出异常,让事务管理器感知到 throw e; } // 返回对应的MyClass实例 return new MyClass(...); }
额外注意事项
- 你的
rollbackFor = { Throwable.class }配置已经覆盖了所有异常类型,所以不管是RestClientException还是其他异常,只要能传播到事务管理器,都会触发回滚。 - 确保你的类被Spring管理:比如标记了
@Service或@Component注解,并且在Spring的组件扫描路径下,否则@Transactional注解不会生效。 - 尽量避免在事务方法内部“吞掉”异常,这是Spring事务不回滚最常见的坑之一。
内容的提问来源于stack exchange,提问作者Sumit Ghosh
相关产品推荐
相关产品推荐

