JPA报错:Detached entity passed to persist 问题排查求助
搞定Hibernate的「detached entity passed to persist」错误
咱们先把这个错误的本质说清楚:你调用entityManager.persist()的时候,传进去的Message实体已经是游离状态了——简单说就是这个实体已经有数据库分配的messageId主键值,但现在不在EntityManager的管理范围内,而persist()只能用来保存完全全新的、还没分配主键的实体,这就冲突了。
结合你的代码来看,大概率是这两种情况触发的:
- 你传入的
Message之前已经被保存过一次(比如之前调用过saveEntity,最后被detach了),现在又拿它再次调用saveEntity用persist存 - 你手动给
Message的messageId设了非0的值(long类型默认是0,Hibernate会把0当成未分配主键,但如果设了一个已经在数据库里存在的主键值,也会触发这个错误)
针对你的代码的修复方案
你的DAO层saveEntity方法只处理了全新实体的保存,没考虑实体可能是游离状态的情况。咱们改一下方法,根据主键值判断用persist还是merge:
@Transactional public void saveEntity(@Nullable T entity) { if (entity == null) { return; } // 因为你的messageId是long基本类型,默认0,所以用0判断是不是新实体 if (entity.getMessageId() == 0) { entityManager.persist(entity); // 新实体用persist } else { // 游离实体用merge,它会把实体重新关联到持久化上下文,自动执行更新 entityManager.merge(entity); } entityManager.flush(); entityManager.detach(entity); }
额外的优化建议
- 把
Message的messageId类型改成Long包装类会更靠谱:因为long基本类型默认是0,如果你的业务里真的有主键为0的合法数据,就会判断错误。改成Long后可以用null来判断是不是新实体,更准确:
对应的判断逻辑也改成:@Id @Basic @GeneratedValue(generator = "MESSAGE_SEQ", strategy = GenerationType.SEQUENCE) @SequenceGenerator(name = "MESSAGE_SEQ", sequenceName = "MESSAGE_ID_SEQ") @Column(name = "MESSAGE_ID") protected Long messageId; // 替换成Long类型if (entity.getMessageId() == null) { entityManager.persist(entity); } else { entityManager.merge(entity); } - 还有个小细节:你的服务层
saveMessage方法里传的是HttpEntity<Message>,但调用saveEntity的时候直接传了entity,这应该是个小失误吧?应该传entity.getBody()来拿到里面的Message对象才对,不然传进去的是HttpEntity包装类,肯定会出问题:public void saveMessage(HttpEntity<Message> entity) throws Exception{ try{ repository.saveEntity(entity.getBody()); // 这里要拿HttpEntity里的实际Message对象 }catch(Exception e){ throw e; } }
内容的提问来源于stack exchange,提问作者PDJ
相关产品推荐
相关产品推荐

