JPA保存关联同一User的多Cart时触发detached entity passed to persist错误求解
问题成因
核心原因是多次save操作在独立事务中执行,User实例在第一次save完成后脱离持久化上下文成为脱管对象,后续save触发级联PERSIST操作时无法处理脱管对象,具体执行流程如下:
- 初始创建的
User是无ID的瞬态实例,不在任何持久化上下文中。 - 执行
orderRepository.save(order1)时,Spring Data JPA的save方法判断Order是新实例,调用EntityManager.persist()操作:- 级联保存关联的Cart实例,再触发Cart上配置的
CascadeType.PERSIST级联保存User - User被持久化生成ID,第一次save操作对应的事务提交,持久化上下文关闭,User变为脱管状态
- 级联保存关联的Cart实例,再触发Cart上配置的
- 执行
orderRepository.save(order2)时,再次触发级联PERSIST操作,此时关联的User是有ID的脱管实例,persist()方法不允许操作脱管对象,直接抛出detached entity passed to persist异常。
仅配置CascadeType.MERGE时,第一次save调用persist操作不会触发MERGE级联,User还是瞬态实例,因此抛出
unsaved transient instance异常;同时配置PERSIST和MERGE时,persist操作优先触发PERSIST级联,仍然会碰到脱管User的问题。
解决方案
- 方案1:给测试方法加@Transactional注解
让三次save操作在同一个事务内执行,持久化上下文全程存活,User始终处于持久态,不会出现脱管问题。 - 方案2:先独立持久化User再关联
修改测试代码,先调用userRepository.save(user)将User变为持久态后,再创建关联该User的Cart和Order实例,后续save操作不会再触发User的级联持久化。
示例代码:@Test public void testRetrieveAllOrders() { // Given User user = new User("John","Smith"); // 先保存User user = userRepository.save(user); Cart cart1 = new Cart(user); Cart cart2 = new Cart(user); Cart cart3 = new Cart(user); Order order1 = new Order(cart1, OrderStatus.CREATED); Order order2 = new Order(cart2, OrderStatus.CREATED); Order order3 = new Order(cart3, OrderStatus.CREATED); orderRepository.save(order1); orderRepository.save(order2); orderRepository.save(order3); // 后续逻辑不变 } - 方案3:移除Cart侧@ManyToOne的级联配置
多对一关联场景下,父实体User的生命周期不应该由子实体Cart控制,直接去掉Cart的user属性上的cascade配置即可,后续按方案2先保存User再使用。
内容的提问来源于stack exchange,提问作者javax
相关产品推荐
相关产品推荐

