一对多关系JPA / Hibernate删除链接

时间:2011-04-12 21:11:31

标签: java hibernate gwt jpa

我的双向关系设置如下:

class Child{
    @ManyToOne
    @JoinTable(name = "CHILDREN_WITH_PARENT", 
            joinColumns = {@JoinColumn(name = "CHILD_ID")}, 
            inverseJoinColumns = {@JoinColumn(name = "PARENT_ID")}
    )
    private Parent parent;
}

class Parent{
    @OneToMany(mappedBy="parent", cascade=CascadeType.ALL)
    Set<Child> childrens = new HashSet<Child>();

    public void persistOrMerge() {
        EntityManager em = entityManager();
        em.getTransaction().begin();
        try {
            if (em.contains(this))
                return;
            if (id == null || id == 0) {
                this.setCreatedDate(new Date());
                em.persist(this);
            } else {
                Parent prev = em.find(Parent.class, this.id);
                if (prev == null) {
                    em.persist(this);
                } else{
                    this.setCreatedDate(new Date());
                    em.merge(this);
                }
            }
            em.flush();
            em.getTransaction().commit();

        }  finally {
            em.close();

        }
    }

}

在我的客户端,我有以下代码(GWT + EntityProxy)

Set<ChildProxy> children  = new HashSet<ChildProxy>();
if(childIsNew)
   child = request.create(Children.class)
else
   child = request.edit(oldChild)
children.add(child);
//If children are deleted, they are not contained in the set
//we are sending back to server
parent.setChildren(children)
parent.persistOrMerge();

此代码仅适用于添加新子代。即使父类接收到空子集,从父项中删除子项也不起作用。 JOIN表中的链接不会被删除。

你能告诉我遗失的地方吗?

谢谢!

4 个答案:

答案 0 :(得分:6)

我首先要说的是,实体是直接使用实体管理器的实体是一个非常糟糕的主意。

EntityManager.merge()方法返回实际的合并实例,这意味着在您的代码中,当您发出

em.merge(this)

您无法保证合并后的实例不再与“this”对应,从那时起您可能会看到各种逻辑问题。

如果您认为这不是什么大问题,那么您的问题应该通过在关系的OneToMany端启用孤儿删除来解决,前提是孩子不会在其他关系中的其他任何地方使用。否则你将不得不手动合并。

@OneToMany(mappedBy="parent", cascade=CascadeType.ALL, orphanRemoval=true)
Set<Child> childrens = new HashSet<Child>();

JPA 2.0规范声明

指定为OneToOne或OneToMany的关联支持使用orphanRemoval 选项。当orphanRemoval生效时,以下行为适用:

  • 如果是实体的目标 这种关系从中删除了 关系(通过设置 关系为null或删除 来自关系的实体 集合),删除操作 将适用于该实体 而成为孤儿。删除操作是 在冲洗时应用 操作。孤儿撤消 功能旨在用于 私人“拥有”的实体 由他们的父实体。手提 否则,申请必须没有 取决于具体的顺序 删除,不得重新分配 孤儿的实体 另一种关系或其他 试图坚持下去。如果是实体 孤儿是一个独立的,新的,或 删除实体,语义 orphanRemoval不适用。
  • 如果应用删除操作 托管源实体,删除 操作将级联到 关系目标 符合第3.2.3节的规定(和 因此没有必要指定 cascade = REMOVE for 关系)[20]。

答案 1 :(得分:1)

// In the entity class of Parent1 write a method to unlink parent2
public void unLinkParent2(Parent2 parent2)
{
    //remove columns from the link table, pertaining to the parent2
    getParent2Collection().remove(parent2);
    // using the parent2 object remove 'this' parent1 entity link columns
    parent2.getParent1Collection().remove(this);
}
Parent1:        Parent2:        LinkP1-P2
--------------------------------------------------
Id1(PK)         Id2(PK)         Id1 (composite PK)
name1           Name2           Id2 (composite PK)
Link表的

Id1Id2是一个引用Parent1Parent2表的主键。

答案 2 :(得分:0)

这个问题是相关的: JPA CascadeType.ALL does not delete orphans

答案 3 :(得分:0)

ManyToOne映射控制关系 - 您已将OneToMany标记为子项与其父项的关系所映射。这意味着只有在从子端合并时才会获取对关系的更改。由于从父列表中删除的子项不再包含在其中,因此无法将merge级联到它们,并且它们中的任何更改都不会持久保存到数据库中。

正如其他人所指出的,孤儿删除将允许在合并时删除列表中没有的任何子项。当你想要的只是关系被淘汰时,这可能是过度的。在这个疤痕中,你可以扭转这种关系,使它由父母拥有。这将确保关系得到更新,但删除的子项中的任何更改仍然无法获取。获取这些更改的唯一方法是将子项添加到合并的新父项,或者单独合并它们。如果它们可以在没有父项的情况下存在,则直接调用它们的persist / merge是更好的选择。