如何在标记为CascadeType.ALL的关系中添加持久化实体?

时间:2012-11-22 13:39:06

标签: java jpa-2.0 eclipselink cascade

我的Entity与双向ManyToMany关系(在同一实体上)标记为CascadeType.ALL

以下是查找Contact实体的方式:

@ManyToMany(cascade= CascadeType.ALL)
private List<Contact> parentContacts = new ArrayList<Contact>();
@ManyToMany(cascade= CascadeType.ALL, mappedBy="parentContacts")
private List<Contact> childContacts = new ArrayList<Contact>();

我在服务器端有一个方法应该保存这个实体:

public AltContact saveContact(EntityManager em, Contact contact, List<Contact> childs) {
    for (Contact c : childs) {
        contact.getChildContacts().add(c);
        c.getParentContacts().add(contact);
    }
    if (contact.getId() == null) {
        em.persist(contact);
    } else {
        contact = em.merge(contact);
    }
    return contact;
}

如果contact列表中的childs或任何实体未被保留,则效果很好。但有时我需要使用已经存在的实体的saveContact方法,当我尝试这样做时,似乎JPA试图保持关系的所有实体然后将引发异常:

Internal Exception: java.sql.SQLIntegrityConstraintViolationException

实际上,它会尝试重新执行并且已经保持实体,因此Contact的ID字段的唯一性将被违反。

怎么能活跃这个?我的方法出了什么问题?

如何让Eclipselink / JPA不会尝试保留属于关系的实体并且已经保留的实体?

1 个答案:

答案 0 :(得分:2)

如果您的父联系实体是新的,但有些子项存在(但已分离),您不希望在父项上调用persist - 这将导致持久化程序级联到导致异常所需的子项立即抛出或刷新/提交。如果您希望在新的父联系人上调用perist,则应尝试查找现有子项并将托管实例与新联系人关联。如果它不存在,你可以单独坚持孩子或允许坚持级联关系为你做。

通常情况下,只需在新的父联系人上调用合并并让提供者确定它应该是更新还是插入更容易。由于合并级联到子级,它们也将根据需要插入或更新。

但要小心使用级联。当您去除父级时,它还会将删除级联到子级,然后级联到其父级集合中的每个父级。如果管理不正确,可能会无意中删除所有联系人。另请注意,导致子列表从列表中删除的更改可能无法正确级联 - 级联合并只能对当前在集合中的实体进行操作,而不是以前在那里的实体。因此,当您从子集合中删除子节点时,可能需要手动调用子节点上的合并,或者确保它们已通过不同的路径进行合并。