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

时间:2015-06-23 07:11:52

标签: hibernate jpa eclipselink orphan orphaned-objects

重要提示:如果您正在阅读此信息,请考虑查看this post以进行深入讨论。

这是一种非常惯常的做法/情况/要求,父母的子女可能会迁移到另一方父母。如果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所持有的员工列表中。

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

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

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

目前正在使用具有JPA 2.1的EclipseLink 2.6.0。

修改

如果Employee实体仅被删除(因此,在删除之后未添加到列表中 - 未迁移到另一个父级但刚刚删除)从反面列表中删除,则删除其对应的行也像往常一样从数据库(orphanRemoval = true)但是当一个Employee实体(子)从其本地父级列表中删除后,将另一个父级列表添加到另一个父级列表中时,该行只会更新(实体的迁移)。

提供商似乎足够智能,可以检测孩子从父母迁移到另一个父母的情况,作为更新。

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

1 个答案:

答案 0 :(得分:3)

JPA specification中记录了这一点。

部分 3.2.4 (摘录):

  

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

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

部分 3.2.2 (摘录):

  

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

     
      
  • 如果X是已移除的实体,则会被管理。
  •   

orphanRemoval JPA javadoc

  

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

orphanRemoval Hibernate docs

  

如果某个实体已从@OneToMany集合中移除或已删除   关联实体从@OneToOne关联中解除引用   如果orphanRemoval,关联实体可以标记为删除   设为true

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

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

您可能也希望查看此question

&#34;如果orphanRemoval在此类关系的反面设置为true,会发生什么?&#34;

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