【问题标题】:Why merging is not cascaded on a one to many relationship为什么合并不是在一对多关系上级联
【发布时间】:2012-10-20 15:11:57
【问题描述】:

我的问题与我的其他问题几乎相似Explicit delete on JPA relationships 但我想进一步简化它以保证更详细的答案。

假设我在父母和孩子之间有一个 OneToMany 关系。

@Entity
public class Parent {
    private String name;
    @OneToMany(mappedBy="owner",cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    private List<Child> children;
}
@Entity
public class Child {
    private String name;
    @ManyToOne
    private Parent owner;
}

在我的 UI 中,我显示了一个父级列表。用户可以选择一位父母,他/她可以编辑其孩子的列表。 为了说明这一点,我使用了一个 currentParent 变量来表示当前编辑的父级。 用户可以选择添加/编辑孩子的名字。

单击按钮后,我将使用 EJB 调用保存新的子列表。

@ViewScoped
public class MyBean(){
    private Parent currentParent;
    @EJB
    private MyEJB myEJB;

    public void saveChanges(){
        myEJB.save(currentParent);
    }
}

我的 EJB 调用如下所示。

@Stateless
public class MyEJB{
    @PersistenceContext(unitName = "MyPU")
    private EntityManager em;

    public void save(Parent parentNew){
        Parent pOld = em.find(entityClass,  p.getId());
        if(pOld !=null){
            pOld.setName(parentNew.getName());
            pOld.setChildren(parentNew.getChildren());
            em.merge(pOld);
        }
    }
}

我的问题是,我可以更改父级的名称,但对子级列表的任何更改都不会反映到数据库中。 它不会更改或执行任何 SQL 来删除/更新/添加子项。

可能是什么原因?

更新

经过一些试验和错误并通过各种链接阅读后,似乎在我的@onetomany 中设置 orphanRemoval 选项可以解决我的问题。 Eclipselink 在合并过程中自动删除孤立行。

JPA 的复杂性就这么多..

@Entity
public class Parent {
    private String name;
    @OneToMany(mappedBy="owner",cascade=CascadeType.ALL, fetch=FetchType.EAGER,orphanRemoval = true)
    private List<Child> children;
}

【问题讨论】:

  • 也许这会有所帮助:stackoverflow.com/questions/10656765/…
  • 嗨,我不确定我是否从那个链接中得到了答案,所以你的意思是我必须遍历集合并一一保存?我实际上认为eclipselink会处理我收藏的保存?例如,我在一个实体中有 5 个孩子,然后我只用两个孩子编辑它,当我调用合并操作时,它将负责删除/添加行。谢谢

标签: java jpa eclipselink ejb-3.1


【解决方案1】:

您的问题What could be the cause?。原因是parentNew 的值内容。如果parentNew 是以下原因之一,则不会影响/更新到子级。

  1. 无参考实例亲子签证。 ---> 会抛出异常。
  2. Parent 参数值有空列表或空列表 Clild --> 这将是你的原因。
  3. Child 不要从 UI 中获取更新的名称。--> 这将是你的原因。

示例。我赶紧写。没关系。

数据库中已经存在数据

Parent                      Child
==================    =======================================
'P001', 'Parent 1'   'C002', 'Child 2', 'P001'
                     'C003', 'Child 3', 'P001'
                     'C001', 'Child 1', 'P001'

更新后:

Parent                      Child
==================          =======================================
'P001', 'Update Parent 1'   'C002', 'Update Child 2', 'P001'
                            'C003', 'Update Child 3', 'P001'
                            'C001', 'Update Child 1', 'P001'

List<Child> clidList = new ArrayList<Child>();
Parent parent = new Parent("P001", "Update Parent 1", clidList);
Child c1 = new Child("C001", "Update Child 1", parent);
Child c2 = new Child("C002", "Update Child 2", parent);
Child c3 = new Child("C003", "Update Child 3", parent);
clidList.add(c1);
clidList.add(c2);
clidList.add(c3);

Parent existingParent = em.find(Parent.class, parent.getId());
existingParent.setName(parent.getName());
existingParent.setChildren(parent.getChildren());
em.merge(existingParent);

【讨论】:

    【解决方案2】:

    这是合并的标准问题。对集合中子对象的更改将被拾取。

    合并调用只能级联列表中的对象,因此对不在列表中的对象所做的更改会丢失。不幸的是,oneToMany 不拥有该关系:孩子的 ManyToOne 拥有。因此,需要合并子实体才能看到更改并将其推送到数据库。子实体是独立的对象,改变时需要单独合并。那,或添加到不同的父/树以合并。

    【讨论】:

    • 任意数量的选项。如果可能的话,一种方法是将它们添加到不同的父级。或者,如果它们不能独立于其父级,则将它们标记为孤立删除,以便在取消引用时将它们删除。或者通过首先找到父级并修复传递给合并的对象中缺少的子级来处理父级的合并调用中的集合差异 - 这甚至在事件中是可行的。或者只是确保应用对从父级移除的子级调用合并。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-03
    • 2011-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多