Spring Data JPA关联实体保存失败:仅单条Post及Category持久化
问题原因
你遇到的org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.psquared.blog.model.Category错误,核心逻辑是:
- 第一次保存p1时,因为Post的
@ManyToOne配置了cascade=CascadeType.ALL,Hibernate会顺带把关联的Category持久化到数据库,此时这个Category对象从「未被数据库管理的临时对象」变成「被EntityManager监控的托管对象」。等这次保存的事务结束后,该Category对象脱离EntityManager的监控,成为「游离对象」。 - 第二次保存p2时,p2关联的还是这个游离状态的Category,由于级联规则是ALL,Hibernate会尝试对这个游离对象执行
persist操作(该操作仅支持未被数据库处理过的临时对象),因此触发报错。
解决办法
给你几个可行的处理方案:
1. 调整级联规则,弃用CascadeType.ALL
如果业务逻辑不需要通过Post来创建Category,就不要用CascadeType.ALL,改成只保留实际需要的级联操作,比如仅允许更新和刷新:
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH}) private Category category;
这样保存p2时,Hibernate会执行merge操作,将游离的Category重新纳入EntityManager监控,不会触发错误的persist操作。
2. 先持久化Category,再关联到Post
先单独把Category保存到数据库,拿到处于托管状态的实例后,再关联到两个Post对象:
// 先保存Category Category javaCategory = new Category("java"); categoryService.save(javaCategory); // 创建并保存两个Post Post p1 = new Post("Spring Boot入门", javaCategory); postService.save(p1); Post p2 = new Post("Java集合详解", javaCategory); postService.save(p2);
此时javaCategory全程处于EntityManager监控状态,保存p2时不会出现游离对象的问题。
3. 将两个Post的保存操作放在同一事务中
如果必须通过Post的级联处理Category,就把两次save操作封装到同一个事务里:
@Service public class PostService { @Transactional public void saveTwoPosts(Post p1, Post p2) { this.save(p1); this.save(p2); } }
然后在CommandLineRunner中调用这个方法:
postService.saveTwoPosts(p1, p2);
同一事务内,Category始终处于托管状态,第二次保存p2时就不会触发错误。
4. 保存p2前重新加载Category
如果Category已经存在于数据库,可以先从数据库重新查询获取托管状态的实例,再关联到p2:
// 保存p1后,重新从数据库加载Category Category managedCategory = categoryService.findById(javaCategory.getId()); Post p2 = new Post("Java集合详解", managedCategory); postService.save(p2);
这样p2关联的是处于托管状态的Category,级联操作就能正常执行。
内容的提问来源于stack exchange,提问作者Cody
相关产品推荐
相关产品推荐

