Important Notice : If you are reading this post, then consider looking into this post too for in-depth discussions.
It is a quite usual practice/situation/requirement where children of a parent may be migrated to another parent. What happens, if orphanRemoval
is set to true
on the inverse side of such relationships?
Consider as an example, any simple one-to-many relationship as follows.
Inverse side (Department) :
@OneToMany(mappedBy = "department", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Employee> employeeList = new ArrayList<Employee>(0);
Owning 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;
While merging an operation/action like the following (where department
is a detached entity supplied by a 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 may better be done/handled using defensive link (relationship) management methods in the associated entities.
A department instance is supplied by a client. It is a detached entity. It can be the same or a different department depending upon an administrative action performed by the client in question. As a result, if the department instance supplied by a client is different from the one held by the current Employee
, then it should be removed first from the list of employees (employeeList
) held by the current old department on the inverse side associated with that Employee
before adding it to the list of employees held by the new department
supplied.
As a guess, the Employee
row should be removed inadvertently from the database while removing the Employee
instance from the list of employees currently being referred to by the employee's department - old department (before this operation has been triggered) i.e. while migrating a child from its parent to another parent, the child needs to be removed from its native parent before it is adopted by another parent and that child row is supposed to be removed inadvertently from the database (orphanRemoval = true
).
The employee row in the database table, however, remains intact with the updated column values. No DML statements except an UPDATE
statement are generated.
Can I consider, migrating children from their parent to another parent in this way, does not inadvertently remove those children from the database table as they should not be?
Currently using EclipseLink 2.6.0 having JPA 2.1.
EDIT:
If an Employee
entity is only deleted (thus, not added to the list after it was deleted - not migrated to another parent but just deleted) from the list on the inverse side, then its corresponding row is deleted from the database too as usual (orphanRemoval = true
) but the row is simply updated, when an Employee
entity (child) is added to the list of another parent after it was deleted from the list of its native parent (migration of the entity).
The provider appears to be smart enough to detect, the migration of children from their parent to another parent, as an update.
The behaviour can be seen identical on both Hibernate (4.3.6 final) and EclipseLink (2.6.0) but it cannot be relied upon, if it is a provider specific behaviour (not portable). I cannot find anything about this behaviour in the JPA spec.