重要提示:如果你正在阅读这篇文章,那么也考虑看看这篇文章进行深入讨论。
这是一个非常常见的做法/情况/要求,其中一个父级的子级可以迁移到另一个父级。如果在这种关系的反面将孤儿删除
设置为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;
在合并如下操作/动作时(其中< code>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
行,同时从员工部门 - 旧部门当前引用的员工列表中删除 Employee
实例(在触发此操作之前),即在将子项从其父级迁移到另一个父级时,需要从其本机父级中删除该子项,然后才能被另一个父级采用,并且该子行应该是无意中从数据库中删除(孤立删除 = true
)。
但是,数据库表中的员工行与更新的列值保持不变。除了UPDATE
语句之外,不生成任何DML语句。
我可以考虑一下,以这种方式将孩子从他们的父母迁移到另一个父母,不会无意中从数据库表中删除这些孩子吗?
目前使用的EclipseLink 2.6.0有JPA 2.1。
编辑:
如果一个员工
实体仅从反向侧的列表中删除(因此,在它被删除后没有添加到列表中-没有迁移到另一个父级,而是刚刚删除),那么它的相应行也像往常一样从数据库中删除(孤儿删除=真
),但当一个员工
实体(子)在从其本机父级列表中删除后添加到另一个父级的列表中时,该行会被简单地更新(实体的迁移)。
提供者似乎足够聪明,能够检测到孩子从父母迁移到另一个父母的情况,作为一个更新。
在HiberNate(4.3.6最终版)和Eclipse Link(2.6.0版)上可以看到相同的行为,但如果它是特定于提供者的行为(不可移植),则不能依赖它。我在JPA规范中找不到有关此行为的任何内容。
这记录在JPA规范中。
第 3.2.4 节(摘录):
应用于实体X的刷新操作的语义如下:
第3.2.2节(节选):
应用于实体X的持久化操作的语义如下:
孤立删除
JPA javadoc:
(可选)是否将删除操作应用于已从关系中删除的实体,并将删除操作级联到这些实体。
< code>orphanRemovalHibernate文档:
如果从@OneToOne
集合中删除了一个实体,或者从@OneToOne
关联中取消了关联实体的引用,则如果孤品移除
设置为true
,则可以将此关联实体标记为删除。
因此,您从部门D1
中删除员工E
并将她添加到部门D2
。
然后Hibernate将部门D1
与数据库同步,发现E
不在员工列表中,并将E
标记为删除。然后,它将D2
与数据库同步,并将PERSIST
操作级联到员工列表中(第3.2.4节)。由于<code>E</code>现在在这个列表中,所以级联应用于它,Hibernate取消计划删除操作(第3.2.2节)。
您可能也想看看这个问题。
“如果在这种关系的反面将orphanRemove
设置为true
,会发生什么?”
您已经在反面设置了它(反面是声明mapedBy
的一面)。如果你的意思是如果它设置在另一边(在这种情况下是@ManyToOne
),那么它就没有意义了,这就是为什么@ManyToOne
和@ManyToOne
中没有这样的属性。