EclipseLink中OneToMany关联场景下禁止操作子实体的解决方案咨询
错误根因说明
你调用merge方法传入的Company实例中,关联的User集合里的对象是未被当前持久化上下文管理的临时/游离对象,JPA在同步数据时检测到这个未托管的关联对象,又没有匹配的级联持久化规则,因此抛出异常。
你之前的配置不生效是因为双向@OneToMany关联中,mappedBy标识了Company侧是关联的反向端,不维护外键,所以你在Company的users属性上加的@JoinColumn、insertable/updatable配置都不会生效;添加CascadeType.PERSIST反而会触发级联保存User,和需求完全相反;fetch类型仅决定关联对象的加载时机,和是否触发保存操作无关。
方案1:全局注解配置(所有操作Company的场景都不会触发User的增改)
- 调整Company实体的
@OneToMany配置,去掉所有级联配置,保持默认级联为空即可:
@OneToMany(mappedBy = "parentCompany", fetch = FetchType.LAZY) private List<User> users;
- 如需彻底禁止关联关系的修改,可在User侧的
@ManyToOne关联上添加insertable=false、updatable=false配置(User是双向关联的维护端,这里的配置才会生效):
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "ID_COMPANIES", insertable = false, updatable = false) private Company parentCompany;
方案2:编程式适配(仅特定update场景禁止关联操作,不影响全局)
如果只有当前的update接口需要禁止保存子实体,不需要修改全局注解,可选择以下两种实现之一:
实现A:手动更新指定字段
先查询得到托管状态的Company实例,仅复制需要更新的字段,忽略users集合,避免触发关联对象检查:
public T update(T t) { Company input = (Company) t; Company managedCompany = entityManager.find(Company.class, input.getId()); // 仅赋值需要更新的字段,不要操作users集合 managedCompany.setCompanyName(input.getCompanyName()); managedCompany.setAddress(input.getAddress()); // 其他需更新字段依次赋值 entityManager.flush(); return (T) managedCompany; }
实现B:merge前清空关联集合
如果一定要使用merge逻辑,可在merge前将传入的Company对象的users集合置空,JPA不会再检测关联的User对象:
public T update(T t) { Company input = (Company) t; input.setUsers(null); return entityManager.merge(input); }
内容的提问来源于stack exchange,提问作者senthil kumar
相关产品推荐
相关产品推荐

