Spring @Transactional事务未回滚问题求助(CrudRepository场景)
事务回滚失效问题排查与解决
问题描述
我给业务方法添加了@Transactional注解,当方法进入catch分支捕获异常后抛出自定义异常,或是主动抛出RuntimeException时,期望此前通过CrudRepository保存到数据库的数据能触发回滚,但实际数据仍留存,即使设置rollbackFor=Exception.class也无法生效,请问如何实现正确的事务回滚?
代码示例
示例1:捕获异常后抛出自定义异常
@Transactional public RpaLog createRpaLogObject(CreateRpaLogObjectRequest request) { RpaLog rpaLog = new RpaLog(); try { rpaLog.setFunctionName(request.getFunctionName()); rpaLog.setStatus(request.getStatus()); rpaLog.setCreatedDate(HelperUtils.getCurrentTimestamp()); rpaLog = rpaLogRepository.save(rpaLog); String externalApiUrl = rpaConfig.getEndpoint() + rpaLog.getId(); restTemplate.getForObject( externalApiUrl, ExternalApiResponse.class ); }catch (Exception e){ throw new CreationFailedException(MessageConstants.RPA_CREATION_FAILED + request.getFunctionName()); } return rpaLog; }
示例2:主动抛出RuntimeException
@Transactional(rollbackFor = Exception.class) public RpaLog createRpaLogObject(CreateRpaLogObjectRequest request) { RpaLog rpaLog = new RpaLog(); rpaLog.setFunctionName(request.getFunctionName()); rpaLog.setStatus(request.getStatus()); rpaLog.setCreatedDate(HelperUtils.getCurrentTimestamp()); rpaLog = rpaLogRepository.save(rpaLog); throw new RuntimeException("Throw example"); }
常见失效原因及解决办法
1. 事务注解未被Spring识别
- 原因:业务类未被Spring容器管理,或者不在Spring组件扫描范围内。
- 解决:给业务类添加
@Service/@Component注解,确保Spring的@ComponentScan路径覆盖该类所在包。
2. 自定义异常类型不符合回滚规则
- 原因:如果
CreationFailedException是受检异常(继承自Exception而非RuntimeException),默认@Transactional只会对运行时异常(RuntimeException)和错误(Error)触发回滚,显式设置rollbackFor=Exception.class才会生效,但要确保注解配置正确。 - 解决:
- 要么让
CreationFailedException继承RuntimeException,无需额外配置即可触发回滚; - 要么在
@Transactional中显式指定回滚的异常类型:@Transactional(rollbackFor = {CreationFailedException.class, Exception.class})。
- 要么让
3. 数据库引擎不支持事务
- 原因:比如MySQL使用了MyISAM引擎,该引擎不支持事务;PostgreSQL等数据库如果未正确配置事务隔离级别也可能有问题。
- 解决:将MySQL表引擎修改为InnoDB,确保数据库本身支持事务操作。
4. 方法内部调用导致AOP代理失效
- 原因:如果是同一个类内部调用带
@Transactional注解的方法,Spring的AOP代理不会生效,事务逻辑不会被触发。 - 解决:
- 将事务方法抽取到单独的Bean中,通过依赖注入调用;
- 或者在当前类中注入自身的代理实例(如通过
@Autowired注入当前类,而非直接this.方法名()调用)。
5. 事务管理器配置错误
- 原因:Spring未正确配置事务管理器,或者事务管理器关联的数据源不正确。
- 解决:检查配置类中是否正确定义了
DataSourceTransactionManager,并关联了业务使用的数据源:
@Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }
修正后的代码示例
示例1修正版
@Service public class RpaLogService { @Autowired private RpaLogRepository rpaLogRepository; @Autowired private RestTemplate restTemplate; @Autowired private RpaConfig rpaConfig; // 显式指定需要回滚的异常类型 @Transactional(rollbackFor = {CreationFailedException.class, Exception.class}) public RpaLog createRpaLogObject(CreateRpaLogObjectRequest request) { RpaLog rpaLog = new RpaLog(); try { rpaLog.setFunctionName(request.getFunctionName()); rpaLog.setStatus(request.getStatus()); rpaLog.setCreatedDate(HelperUtils.getCurrentTimestamp()); rpaLog = rpaLogRepository.save(rpaLog); String externalApiUrl = rpaConfig.getEndpoint() + rpaLog.getId(); restTemplate.getForObject(externalApiUrl, ExternalApiResponse.class); } catch (Exception e) { throw new CreationFailedException(MessageConstants.RPA_CREATION_FAILED + request.getFunctionName()); } return rpaLog; } }
示例2修正版
@Service public class RpaLogService { @Autowired private RpaLogRepository rpaLogRepository; @Transactional(rollbackFor = Exception.class) public RpaLog createRpaLogObject(CreateRpaLogObjectRequest request) { RpaLog rpaLog = new RpaLog(); rpaLog.setFunctionName(request.getFunctionName()); rpaLog.setStatus(request.getStatus()); rpaLog.setCreatedDate(HelperUtils.getCurrentTimestamp()); rpaLog = rpaLogRepository.save(rpaLog); throw new RuntimeException("Throw example"); } }
内容的提问来源于stack exchange,提问作者Riki Ahmad
相关产品推荐
相关产品推荐

