Spring Data JPA多对多关联绑定已查询实体保存时报detached entity错误
异常含义
org.hibernate.PersistentObjectException: detached entity passed to persist 异常本质是Hibernate执行持久化操作时收到了脱管状态的实体:
- 你通过
userTypeRepository.findAll()查询出的UserType实体,在查询方法执行结束后,对应事务提交、持久化上下文关闭,这些实体就进入了脱管状态,不属于当前保存User操作的持久化上下文 - 你在User侧的
@ManyToMany注解配置了CascadeType.ALL,该配置包含了CascadeType.PERSIST级联持久化规则,保存新的User(瞬时状态)时,Hibernate会自动对关联的UserType执行persist操作,而persist操作要求入参必须是瞬时状态的实体,传入脱管实体就会触发该异常
修复方案
方案1:移除不必要的级联配置(最推荐)
user_type是预填充的基础字典表,本身不需要通过User的操作级联新增、修改、删除数据,直接去掉User侧多对多关联的级联配置即可:
将原有配置
@ManyToMany(cascade = { CascadeType.ALL }) @JoinTable(name = "portal_user_user_type", /* 其余关联配置 */) private List<UserType> userTypes;
修改为
@ManyToMany @JoinTable(name = "portal_user_user_type", /* 其余关联配置 */) private List<UserType> userTypes;
修改后保存User时,Hibernate只会向中间表portal_user_user_type插入关联数据,不会操作user_type表,完全符合需求。
方案2:同事务内查询UserType
如果业务需求必须保留CascadeType.ALL配置,可以在你的业务方法上添加@Transactional注解,让查询UserType和保存User的逻辑在同一个事务内执行,这样查询出的UserType属于当前持久化上下文的持久化状态,级联persist时不会抛出异常。
方案3:使用merge操作(不推荐)
也可以通过entityManager.merge(user)替代repository的save操作,merge操作会自动将脱管实体合并到当前持久化上下文。但该方案存在风险:如果脱管状态的UserType属性被修改过,会同步更新到user_type表,不符合基础字典表不允许随意修改的要求,因此不建议使用。
内容的提问来源于stack exchange,提问作者AndreaNobili
相关产品推荐
相关产品推荐

