一尘不染

将子从其父迁移到另一个父时,将orphanRemoval设置为true

hibernate

这是一种很常见的做法/情况/要求,其中父母的子女可以迁移到另一父母。如果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,则应首先从employeeList当前
部门所拥有的雇员列表中与之相反的一方将其删除,然后Employee再添加将其添加到新department供应商所拥有的员工列表中。

可以猜测,在EmployeeEmployee雇员部门-
旧部门当前所引用的雇员列表中删除实例时(在触发此操作之前),即在将孩子从其父项迁移到父项时,应无意中从数据库中删除该行。另一个父级,则需要先从其父级父级中删除该子级,然后再不经意地从数据库(orphanRemoval= true)中删除该子行。

但是,数据库表中的雇员行与更新的列值保持不变。除一条语句外,UPDATE不生成任何DML语句。

我是否可以考虑以这种方式将子代从其父代迁移到另一个父代,是否会无意间从数据库表中删除那些子代, 而不 应该 这样 做?

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


编辑:

如果Employee仅从相反侧的列表中删除一个实体(因此,在删除该实体后不将其添加到列表中-
不会迁移到另一个父级,而只是删除了),则该对象对应的行也照常从数据库中删除(orphanRemoval = true)但是,当Employee实体(子级)从其本机父级列表中删除后,该实体(子级)被添加到另一个父级列表中时,该行只是被更新(实体的迁移)。

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

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


阅读 397

收藏
2020-06-20

共1个答案

一尘不染

JPA规范中对此进行了记录。

3.2.4 节(节选):

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

  • 如果X是受管实体,则将其同步到数据库。
    • 对于由X的关系引用的所有实体Y,如果已使用级联元素值Cascade = PERSIST或Cascade =
      ALL注释了与Y的关系,则对Y应用持久性操作

3.2.2 节(节选):

应用于实体X的persist操作的语义如下:

  • 如果X是已删除的实体,则它将被管理。

orphanRemovalJPA
javadoc

(可选)是否 将删除操作 应用于已从关系中删除的实体,以及是否将删除操作应用于这些实体。

orphanRemovalhibernate文档

如果一个实体从删除@OneToMany集合或相关实体是由非关联@OneToOne的关联,这种关联的实体可以 标记为删除
,如果orphanRemoval设置为true

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

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

您可能还想看看这个问题。

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

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

2020-06-20