Spring Data多save操作下@Transactional事务不回滚问题求助
解决Spring Data事务未回滚及即时写入数据库的问题
嘿,我碰到过类似的坑,咱们一步步拆解问题,帮你搞定它:
一、先搞懂为啥事务没回滚?
@Transactional默认只对RuntimeException及其子类触发回滚,DataIntegrityViolationException本身属于RuntimeException,按道理应该回滚,但大概率是这几个细节出了问题:
- 注解位置错了:如果
@Transactional标在了private方法上,Spring AOP根本没法代理私有方法,事务等于白加。一定要把注解放在public方法上。 - 异常被你吞了:要是服务方法里用try-catch把
DataIntegrityViolationException接住了,却没重新抛出来,Spring根本感知不到异常发生,自然不会触发回滚。比如这种错误写法:@Transactional public void saveTwoModels() { testRepo.save(testModel); try { testRepo2.save(testModel2); // 这里抛异常 } catch (DataIntegrityViolationException e) { // 只打了日志或者啥也没做,没重新抛出 } } - 事务传播属性搞砸了:如果这个方法被另一个事务方法调用,传播属性比如
PROPAGATION_SUPPORTS/PROPAGATION_NOT_SUPPORTED会导致当前事务不生效。
二、为啥每个save()后数据直接写入数据库?
这是Hibernate的flush时机提前了!默认Hibernate会在事务提交前统一flush,但有些场景下会提前触发:
- Spring Data的
save方法,当你保存的实体是自增主键时,Hibernate必须立刻flush才能拿到生成的主键值,这时候数据就直接写入库了。 - 事务没生效的情况下,HikariCP的自动提交是开着的,每个save操作都会自动提交,自然立刻写入数据库。
三、具体解决步骤
1. 确保事务真的生效
- 把
@Transactional放在public方法上,而且必须是通过Spring容器的Bean调用(内部方法直接调用的话,AOP代理不生效)。要是必须内部调用,就自己注入当前Bean:@Service public class TestService { @Autowired private TestRepo testRepo; @Autowired private TestRepo2 testRepo2; @Autowired private TestService self; // 注入自己 public void outerCall() { self.saveWithTransaction(); // 通过代理调用事务方法 } @Transactional public void saveWithTransaction() { testRepo.save(testModel); testRepo2.save(testModel2); // 抛异常时会回滚 } }
2. 别让异常“消失”
- 要么别捕获
DataIntegrityViolationException,要么捕获后重新抛出RuntimeException:@Transactional public void saveTwoModels() { testRepo.save(testModel); try { testRepo2.save(testModel2); } catch (DataIntegrityViolationException e) { throw new RuntimeException("保存第二个模型失败", e); // 重新抛出让Spring感知 } } - 也可以直接指定回滚异常类型,更保险:
@Transactional(rollbackFor = DataIntegrityViolationException.class)
3. 控制Hibernate的flush时机
- 用
EntityManager的persist代替Spring Data的save,persist不会立刻flush,直到事务提交才会统一写入:@Transactional public void saveTwoModels() { entityManager.persist(testModel); entityManager.persist(testModel2); // 事务提交时才会批量flush } - 检查HikariCP配置,确保自动提交是关闭的:
spring.datasource.hikari.auto-commit=false
4. 确认事务配置开启
- 如果你用的是Spring Boot,默认已经开启事务管理;如果是传统Spring项目,要加
@EnableTransactionManagement注解。
最后总结
先验证事务是否真的生效(可以加日志或者用Spring Boot Actuator看事务状态),再确保异常没被吞,最后控制flush时机,这样第二个save抛异常时,第一个save的数据也会被回滚啦。
内容的提问来源于stack exchange,提问作者Mariusz.v7
相关产品推荐
相关产品推荐

