6

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


这是一种非常常见的做法/情况/要求,父母的孩子可能会迁移到另一位父母。如果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持有的员工列表中删除,然后再添加它将新供应的员工名单列入。Employeedepartment

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

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

我是否可以考虑,以这种方式将孩子从他们的父母迁移到另一个父母,不会无意中从数据库表中删除这些孩子,因为他们不应该这样做?

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


编辑:

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

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

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

4

1 回答 1

3

这记录在JPA 规范中。

3.2.4节(摘录):

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

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

3.2.2节(摘录):

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

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

orphanRemovalJPA javadoc

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

orphanRemoval休眠文档

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

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

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

你可能也想看看这个问题

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

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

于 2015-07-03T12:37:48.597 回答