将 children 从他们的 parent 迁移到另一个 parent 时将 orphanRemoval 设置为 true

Setting orphanRemoval to true while migrating children from their parent to another parent

重要提示: 如果您正在阅读这篇文章 post,请考虑查看 以进行 in-depth 讨论。


这是一个很常见的 practice/situation/requirement,其中一个 parent 的 children 可能会迁移到另一个 parent。如果在这种关系的反面将 orphanRemoval 设置为 true,会发生什么情况?

以任何简单的 one-to-many 关系为例。

反面(部门):

@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;

在像下面那样合并 operation/action 时(其中 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);

当然,添加和删除员工可能更好done/handled在关联实体中使用防御性link(关系)管理方法。

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

作为猜测,Employee 行应该从数据库中无意中删除,同时从员工部门当前引用的员工列表中删除 Employee 实例 - 旧部门(之前此操作已被触发)即,在将 child 从其 parent 迁移到另一个 parent 时,需要先从其原生 parent 中删除 child被另一个 parent 采用,并且 child 行应该被无意中从数据库中删除 (orphanRemoval = true)。

但是,数据库中的员工行 table 保持不变,并具有更新后的列值。除 UPDATE 语句外,没有生成任何 DML 语句。

我可以考虑一下,以这种方式将 children 从他们的 parent 迁移到另一个 parent,不会无意中从数据库中删除那些 children table 他们应该不是

目前正在使用带有 JPA 2.1 的 EclipseLink 2.6.0。


编辑:

如果 Employee 实体仅从相反侧的列表中删除(因此,在删除后未添加到列表中 - 未迁移到另一个 parent 而只是删除),然后它的相应行也像往常一样从数据库中删除(orphanRemoval = true)但是当一个Employee实体(child)被添加到另一个[=的列表时,该行被简单地更新了85=] 从其原生列表中删除后 parent (实体迁移)。

提供商似乎足够聪明,可以检测 children 从他们的 parent 迁移到另一个 parent,作为更新。

在 Hibernate(4.3.6 final)和 EclipseLink(2.6.0)上可以看到相同的行为,但不能依赖它,如果它是提供者特定的行为(不是 portable) .我在 JPA 规范中找不到有关此行为的任何信息。

这在 JPA specification 中有记录。

3.2.4 节(摘录):

The semantics of the flush operation, applied to an entity X are as follows:

  • If X is a managed entity, it is synchronized to the database.
    • For all entities Y referenced by a relationship from X, if the relationship to Y has been annotated with the cascade element value cascade=PERSIST or cascade=ALL, the persist operation is applied to Y

3.2.2 节(摘录):

The semantics of the persist operation, applied to an entity X are as follows:

  • If X is a removed entity, it becomes managed.

orphanRemoval JPA javadoc:

(Optional) Whether to apply the remove operation to entities that have been removed from the relationship and to cascade the remove operation to those entities.

orphanRemoval Hibernate docs:

If an entity is removed from a @OneToMany collection or an associated entity is dereferenced from a @OneToOne association, this associated entity can be marked for deletion if orphanRemoval is set to true.

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

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

您可能还想看看这个 question

"What happens, if orphanRemoval is set to true on the inverse side of such relationships?"

你已经设置反面了(反面就是声明mappedBy的那一面)。如果你的意思是如果它被设置在 other 一侧(在这种情况下为 @ManyToOne),那么它就没有意义,这就是为什么 @ManyToOne@ManyToMany.