【问题标题】:Setting orphanRemoval to true while migrating children from their parent to another parent将子项从父项迁移到另一个父项时将 orphanRemoval 设置为 true
【发布时间】:2015-06-23 07:11:52
【问题描述】:

重要通知:如果您正在阅读这篇文章,请考虑查看this post 进行深入讨论。


父母的孩子可能会迁移到另一位父母,这是一种非常常见的做法/情况/要求。如果 orphanRemoval 在这种关系的反面设置为 true 会发生什么?

以任何简单的一对多关系为例,如下所示。

反面(部门):

@OneToMany(mappedBy = "department", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Employee> employeeList = new ArrayList<Employee>(0);

拥有方(员工):

@JoinColumn(name = "department_id", referencedColumnName = "department_id")
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
private Department department;

在合并如下操作/动作时(其中department 是客户端提供的分离实体),

Employee employee = entityManager.find(Employee.class, 1L);
Department newDepartment = entityManager.contains(department) ? department : entityManager.merge(department);

if (!newDepartment.equals(employee.getDepartment())) {
    employee.getDepartment().getEmployeeList().remove(employee);
    // Since orphanRemoval is set to true, 
    // this should cause a row from the database table to be removed inadvertently
    // by issuing an addition DELETE DML statement.
}

employee.setDepartment(newDepartment);
employee.setEmployeeName("xyz");        

List<Employee> employeeList = newDepartment.getEmployeeList();

if (!employeeList.contains(employee)) {
    employeeList.add(employee);
}

entityManager.merge(employee);

当然,添加和删除员工可能会更好地使用关联实体中的防御性链接(关系)管理方法来完成/处理。

部门实例由客户提供。它是一个分离的实体。它可以是相同或不同的部门,具体取决于相关客户执行的管理操作。因此,如果客户端提供的部门实例与当前Employee 持有的部门实例不同,则应首先将其从当前oldemployeeList)中删除/strong> 与该 Employee 关联的反面部门,然后将其添加到提供的新 department 持有的员工列表中。

作为一种猜测,Employee 行应该被无意中从数据库中删除,同时从员工的部门当前引用的员工列表中删除 Employee 实例 - 旧部门(在触发此操作之前)即,在将孩子从其父级迁移到另一个父级时,该子级需要在被另一个父级收养之前从其本地父级中删除,并且该子行应该会无意中从数据库中删除(orphanRemoval = true)。

但是,数据库表中的员工行与更新后的列值保持不变。除了UPDATE 语句之外,不会生成任何 DML 语句。

我是否可以考虑,以这种方式将子级从其父级迁移到另一个父级,不会无意中将这些子级从数据库表中删除,因为它们应该

当前使用具有 JPA 2.1 的 EclipseLink 2.6.0。


编辑:

如果 Employee 实体仅从反面的列表中删除(因此,在删除后未添加到列表中 - 未迁移到另一个父级,而是刚刚删除),则其对应的行从列表中删除数据库也像往常一样(orphanRemoval = true),但是当一个Employee实体(子)从其原生父级列表中删除后被添加到另一个父级的列表中时(实体的迁移),该行被简单地更新.

提供者似乎足够聪明,可以检测到孩子从他们的父母迁移到另一个父母,作为更新。

在 Hibernate(4.3.6 最终版)和 EclipseLink(2.6.0)上可以看到相同的行为,但如果它是提供程序特定的行为(不可移植),则不能依赖它。我在 JPA 规范中找不到有关此行为的任何信息。

【问题讨论】:

    标签: hibernate jpa eclipselink orphan orphaned-objects


    【解决方案1】:

    这在JPA specification 中有记录。

    3.2.4 节(摘录):

    应用于实体 X 的刷新操作的语义为 如下:

    • 如果 X 是托管实体,它会同步到数据库。
      • 对于由来自 X 的关系引用的所有实体 Y,如果与 Y 的关系已使用级联元素值进行注释 cascade=PERSIST 或 cascade=ALL,持久化操作应用于 Y

    3.2.2 节(摘录):

    持久化操作的语义,应用于实体 X 是 如下:

    • 如果 X 是一个已移除的实体,它就会变为托管的。

    orphanRemoval JPA javadoc:

    (可选)是否将删除操作应用于以下实体 已从关系中删除并级联删除 对这些实体进行操作。

    orphanRemoval Hibernate docs:

    如果实体从@OneToMany 集合或 关联实体从 @OneToOne 关联中取消引用,这 如果orphanRemoval 是,则关联实体可以标记为删除 设置为true

    因此,您将员工 E 从部门 D1 中删除,并将她添加到部门 D2

    Hibernate 然后将部门D1 与数据库同步,发现E 不在员工列表中,并将E 标记为删除。然后它将D2 与数据库同步并将PERSIST 操作级联到员工列表(第3.2.4 节)。由于E 现在在此列表中,因此对其应用级联,Hibernate 取消安排删除操作(第 3.2.2 节)。

    您可能还想看看这个question

    “如果 orphanRemoval 在这种关系的反面设置为 true,会发生什么?”

    您已经将它设置在反面(反面是声明mappedBy 的那一面)。如果你的意思是如果它被设置在 other 一侧(在这种情况下为@ManyToOne),那么它没有意义,这就是为什么在@ManyToOne 和@987654345 中没有这样的属性@。

    【讨论】:

    • 我想知道规范和链接的问题与原始帖子有关,原始帖子是关于将子对象从其父对象迁移到另一个父对象。
    • @Tiny 请查看我编辑的答案。希望我现在更清楚了。
    • 谢谢。那么我们可以得出结论,这是一个特定于 JPA 且独立于提供者的记录行为吗?因此,它不是特定于提供者的行为。 (拥有方的级联 (Employee) 即 cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH} 仅用于装饰。顺便说一句,如果完全删除,它的行为没有区别)。
    • 是的,这是一个记录在案的、独立于提供者的行为。如果您从employeeList 中删除级联(或将其更改为ALLPERSIST 以外的其他内容),行为上的差异将是。你能试试吗?
    • 我试了一下,删除了关系反面的cascade = CascadeType.ALLorphanRemoval = true (Department)。除了orphanRemoval 功能被禁用(默认情况下)这一事实之外,它没有任何区别。发布了相同的UPDATE DML 声明。 (目前在 EclipseLink (2.6.0) 上进行了尝试,但希望 Hibernate 应该以同样的方式进行)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-09
    • 1970-01-01
    相关资源
    最近更新 更多