hibernate - 将 child 从其 parent 迁移到另一个 parent 时将 orphanRemoval 设置为 true

标签 hibernate jpa eclipselink orphan orphaned-objects

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

<小时/>

parent 的 child 可能会迁移到另一位 parent 身边,这是一种很常见的做法/情况/要求。如果在此类关系的反面将 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;

在合并如下所示的操作/ Action 时(其中部门是客户端提供的独立实体),

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持有的部门实例不同,则应首先将其从员工列表(employeeList)中删除。 )由与该员工相关联的当前部门持有,然后将其添加到由提供的新部门持有的员工列表中.

据猜测,在从员工所在部门当前引用的员工列表中删除 Employee 实例时,应该会无意中从数据库中删除 Employee 行 -旧部门(在触发此操作之前),即在将子项从其父项迁移到另一个父项时,需要先将子项从其 native 父项中删除,然后再由另一个父项收养,并且该子行应该会无意中从其父项中删除数据库(orphanRemoval = true)。

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

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

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

<小时/>

编辑:

如果一个Employee实体只是从反面的列表中删除(因此,在删除后没有添加到列表中 - 没有迁移到另一个父级,而是只是删除),那么它对应的实体行也会照常从数据库中删除 (orphanRemoval = true),但当 Employee 实体(子级)添加到另一个父级的列表时,该行会被简单更新从其 native 父级列表中删除它之后(实体的迁移)。

提供商似乎足够聪明,能够检测到子级从其父级迁移到另一个父级的情况,作为更新。

在 Hibernate(4.3.6 Final)和 EclipseLink(2.6.0)上可以看到该行为是相同的,但如果它是提供者特定的行为(不可移植),则不能依赖它。我在 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.

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

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

您可能想看看这个question也是如此。

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

您已经将其设置在反面(反面是声明mappedBy的一面)。如果您的意思是如果它设置在另一侧(在本例中是@ManyToOne),那么它就没有意义,这就是为什么在中没有这样的属性@ManyToOne@ManyToMany

关于hibernate - 将 child 从其 parent 迁移到另一个 parent 时将 orphanRemoval 设置为 true,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30996141/

相关文章:

java - Spring Hibernate 处理 session 中修改查询的最佳方式

java - 具有双参数的 JPA Criteria api 函数

java - 如何在JPA/Hibernate中根据两个外键生成id?

java - JPA多持久化持久单元和JTA事务

java - PostgreSQL 查询在 pgAdmin 中有效,但在 Java EclipseLink 中无效

java - Hibernate + MSSQL,无法将值 NULL 插入列中

java - PersistenceContext 没有注入(inject) EntityManager

java - 需要什么 hibernate/jpa 注解

java - 标准 eclipselink 加入

java - 什么时候不使用 EntityManager.flush()?