SpringBoot中Hibernate同事务下未持久化全部实体问题排查
环境信息
使用Hibernate 5.4.22.Final + SpringBoot 2.3.5.RELEASE
代码结构
Controller层代码
@PostMapping("/insert/{id}") public MyReposneEntitiy insertAutoAndSendNotification(@PathVariable("id") int id, @Valid @RequestBody Auto auto) { MyReposneEntitiy e = service.insertAuto(auto, id); service.sendNotification(id); return e; }
核心Service层代码
@Transactional public MyReposneEntitiy insertAuto(Auto auto, int id) { InfoForAuto info = infoRepo.findById(id).get(); entityManager.detach(info); //该实体标注了@Version,若在持久化上下文则无法修改,因此先detach再保存 info.setVersion(auto.getVersion()); infoRepo.save(info); Person person = personService.createPerson(auto.getPerson()); Eng eng = engService.createEng(auto.getEng(), person.getId()); info.setMSDNumber(auto.getMSDNumber()); info.setStatus(Constants.OK); MyReposneEntitiy response = new MyReposneEntitiy (); response.setStatus(Constants.OK); return response; } public void sendNotification(int id){ //some code... }
被调用的子Service方法
@Transactional public PersonResponse createPerson(PersonInput pesronInput) {...} @Transactional public EngResponse createEng(EngInput engInput, Integer personId) {...}
问题现象
调试时,insertAuto()执行完毕进入sendNotification()第一行时,数据库中person、eng记录已创建,但info实体的更新未生效。需要info的修改在调用sendNotification前完成,同时疑惑为何同事务下没有实现全量提交或回滚的一致性。
问题根源与解决方案
1. info实体未更新的核心原因
你对info的操作存在两个关键问题:
- 执行
entityManager.detach(info)后,info脱离了Hibernate的持久化上下文,后续修改不会被自动追踪。 - 调用
infoRepo.save(info)时,Hibernate会对detached实体执行merge操作,返回一个新的持久化状态实例,但你没有接收这个返回值,后续修改的仍然是原来的detached实例,因此这些修改不会被同步到数据库。
而createPerson和createEng的操作能提前生效,是因为它们操作的是全新实体,方法内的save操作会触发Hibernate的自动flush,且这些实体处于持久化上下文的追踪中。
2. 事务一致性的误解
当前并非事务没有保证一致性,而是info的修改根本没有被纳入事务的同步范围。只要修正info的持久化状态,整个事务内的所有操作(info更新、person/eng创建)会在事务提交时统一执行,自然满足要么全成功要么全回滚的特性。
3. 具体修复步骤
步骤一:修正info的持久化实例引用
将infoRepo.save(info)的调用改为接收返回的持久化实例,确保后续修改的是被Hibernate追踪的对象:
// 替换原有的infoRepo.save(info); info = infoRepo.save(info);
步骤二:提前触发flush(可选)
如果需要在调用sendNotification前就让info的修改立即同步到数据库(而非等事务提交),可以在insertAuto方法末尾手动触发Hibernate的flush操作:
entityManager.flush();
步骤三:优化事务传播配置(可选)
如果createPerson和createEng不需要独立事务,建议去掉它们的@Transactional注解,或者将事务传播行为设置为SUPPORTS,避免不必要的事务代理干扰外层事务的执行流程:
@Transactional(propagation = Propagation.SUPPORTS) public PersonResponse createPerson(PersonInput pesronInput) {...}
内容的提问来源于stack exchange,提问作者Ewe

