1

我有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 不尝试持久化作为关系的一部分并且已经持久化的实体?

4

1 回答 1

2

如果您的父联系人实体是新的,但一些子实体存在(但已分离),您不想在父实体上调用persist - 这将导致persist 级联到子实体,这是引发异常所必需的立即或在刷新/提交时。如果您希望在新的父联系人上调用 perist,那么您应该尝试找到现有的子级并将托管实例与新联系人相关联。如果它不存在,您可以单独持久化子对象或允许持久化关系级联来为您执行此操作。

不过,一般来说,只需对新的父联系人调用 merge 并让提供者确定它应该是更新还是插入要容易得多。由于合并级联到子级,它们也将被适当地插入或更新。

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

于 2012-11-22T15:39:15.813 回答