Setting orphanRemoval to true when transferring children from parent to another parent

Important Note: If you are reading this post, then review this post for in-depth discussions.


This is a common practice / situation / requirement where the children of a parent can be transferred to another parent. What happens if orphanRemoval set to true on the back of such a relationship?

As an example, consider any simple one-to-many relationship as follows.

Reverse side (department):

 @OneToMany(mappedBy = "department", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) private List<Employee> employeeList = new ArrayList<Employee>(0); 

Own side (Employee):

 @JoinColumn(name = "department_id", referencedColumnName = "department_id") @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH}) private Department department; 

When combining an operation / action similar to the following (where department is a separate entity provided by the client),

 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); 

Of course, adding and removing an employee can be better done / processed using security management methods in related objects.

A department instance is provided by the customer. This is a separate facility. This may be the same or a different department depending on the administrative action performed by the client. As a result, if the department instance provided by the client is different from the instance stored in the current Employee , you must first remove it from the list of employees ( employeeList ) held by the current old on the back of this Employee before adding it to the list of employees, stored in the new department .

As you can imagine, the Employee row should be accidentally deleted from the database when deleting the Employee instance from the list of employees currently referenced by the department of the employee department - the old department (before this operation was started) i.e. when transferring a child from the parent to another parent, the child must be deleted from its parent before it is accepted by the other parent, and this child row must be accidentally deleted from the database ( orphanRemoval = true ).

However, the employee row in the database table remains unchanged with updated column values. No DML statements are created except for the UPDATE .

Can I consider the possibility of transferring children from my parent to another parent method in such a way that it does not accidentally delete these children from the database table, since they should not be?

Currently using EclipseLink 2.6.0 with JPA 2.1.


EDIT:

If the Employee object is deleted (thus, it is not added to the list after its removal - it is not transferred to the other parent, but simply deleted) from the list on the back, then its corresponding row is deleted from the database as usual ( orphanRemoval = true ) , but the line is simply updated when the Employee object (child) is added to the list of another parent after it is removed from the list of its parent (migration of the object),

The provider seems smart enough to detect the migration of children from its parent to another parent as an update.

The behavior can be seen the same on both Hibernate (4.3.6 final) and EclipseLink (2.6.0), but you cannot rely on it if it is the behavior of a specific provider (not portable). I cannot find anything like this in the JPA specification.

+6
source share
1 answer

This is described in the JPA specification .

Section 3.2.4 (excerpt):

The semantics of the flash operation applied to object X, as follows:

  • If X is a managed entity, it synchronizes with the database.
    • For all objects Y referenced by a link from X, if the relation to Y was annotated with the value of the cascade element cascade = PERSIST or cascade = ALL, the save operation is applied to Y

Section 3.2.2 (excerpt):

The semantics of the persist operation applied to the X object, as follows:

  • If X is a remote object, it becomes manageable.

orphanRemoval JPA javadoc :

(not necessary). apply the delete operation to objects that have been removed from the relationship and cascade the delete operation with these objects.

orphanRemoval Hibernate docs :

If an object is removed from the @OneToMany collection or a related object is dereferenced from the @OneToOne association, this related object can be marked for deletion if orphanRemoval set to true .

So, you remove employee E from department D1 and add him to department D2 .

Hibernate then synchronizes the D1 department with the database, sees that E not in the list of employees and the E label for deletion. Then it synchronizes D2 with the database and the PERSIST cascade of operations with the list of employees (section 3.2.4). Since E now in this list, the cascade is applied to it, and Hibernate does not assign a delete operation (section 3.2.2).

You might want to look at the question .

"What happens if orphanRemoval set to true on the back of such a relationship?"

You have already installed it on the back side (the back side is the one that declares mappedBy ). If you mean that if it were installed on the other side ( @ManyToOne in this case), then that would not make sense and why there is no such attribute in @ManyToOne and @ManyToMany .

+3
source

Source: https://habr.com/ru/post/989583/


All Articles