重要提示:如果您正在阅读这篇文章,那么也可以考虑查看这篇文章进行深入讨论。
父母的子女可能迁移到另一个父母,这是一种非常常见的做法/情况/要求。如果在这种关系的相反侧将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
所持有的部门实例不同,则应首先将其从与该Employee
相关联的相反侧的当前旧部门所持有的员工列表(employeeList
)中删除,然后再将其添加到所提供的新department
所持有的人员列表中。
作为猜测,Employee
行应该在从员工所在部门当前引用的员工列表中删除Employee
实例时(在触发此操作之前),即在将子级从其父级迁移到另一个父级时,无意中从数据库中删除,子行在被另一个父行采用之前需要从其本机父行中移除,并且该子行被认为是无意中从数据库中移除的(orphanRemoval = true
)。
但是,数据库表中的employee行与更新的列值保持不变。除UPDATE
语句外,不生成任何DML语句。
我是否可以考虑,以这种方式将子级从其父级迁移到另一个父级,不会无意中从数据库表中删除这些子级,因为它们应该是而不是?
目前使用的EclipseLink 2.6.0具有JPA 2.1。
编辑:
如果一个Employee
实体只是从相反侧的列表中删除(因此,在它被删除后没有添加到列表中——没有迁移到另一个父实体,而是刚刚被删除),那么它的对应行也像往常一样从数据库中删除(orphanRemoval = true
),但该行只是被更新,当CCD_ 14实体(子实体)从其本机父实体的列表中删除后被添加到另一个父实体的名单中时(实体的迁移)。
该提供程序似乎足够聪明,可以检测到孩子从父母迁移到另一个父母,作为更新。
在Hibernate(4.3.6最终版本)和EclipseLink(2.6.0)上可以看到相同的行为,但如果它是特定于提供商的行为(不可移植),则不能依赖它。我在JPA规范中找不到任何关于这种行为的信息。
这在JPA规范中有文档记录。
3.2.4节(摘录):
应用于实体X的flush操作的语义如下如下:
- 如果X是托管实体,则会将其同步到数据库。
- 对于由来自X的关系引用的所有实体Y,如果与Y的关系已用级联元素值进行了注释cascade=PERSIST或cascade=ALL,持久操作应用于Y
章节3.2.2(摘录):
应用于实体X的持久化操作的语义如下如下:
- 如果X是一个已删除的实体,则它变为托管实体
orphanRemoval
JPA javadoc:
(可选)是否将移除操作应用于已从关系中删除,并级联删除对这些实体的操作。
orphanRemoval
休眠文档:
如果从
@OneToMany
集合或关联实体从@OneToOne
关联中取消引用,这如果orphanRemoval
为设置为CCD_ 20。
因此,您将员工E
从部门D1
中删除,并将她添加到部门D2
中。
然后Hibernate将部门D1
与数据库同步,发现E
不在员工列表中,并将E
标记为删除。然后,它将D2
与数据库同步,并将PERSIST
操作级联到员工列表中(第3.2.4节)。由于E
现在在该列表中,级联应用于它,Hibernate取消调度删除操作(第3.2.2节)。
你可能也想看看这个问题。
"如果orphanRemoval
在这种关系的相反一侧设置为true
,会发生什么?"
您已经将其设置在反面(反面是声明mappedBy
的一面)。如果你的意思是,如果它被设置在另一个侧(在这种情况下是@ManyToOne
),那么它就没有意义了,这就是为什么@ManyToOne
和@ManyToMany
中没有这样的属性。