Установка orphanRemoval в true при переносе детей от их родителей к другому родителю
Важное замечание: если вы читаете этот пост, подумайте о том, чтобы заглянуть в этот пост слишком подробно.
Это довольно обычная практика / ситуация / требование, когда дети одного из родителей могут быть перемещены к другому родителю. Что будет, если 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
).
Однако строка сотрудника в таблице базы данных остается неизменной с обновленными значениями столбца. Нет операторов DML, кроме UPDATE
оператор генерируется.
Могу ли я подумать, что миграция детей от их родителей к другому родителю таким образом не приведет к непреднамеренному удалению этих детей из таблицы базы данных, как это должно быть?
В настоящее время используется EclipseLink 2.6.0 с JPA 2.1.
РЕДАКТИРОВАТЬ:
Если Employee
сущность только удаляется (то есть, не добавляется в список после того, как была удалена - не переносится на другого родителя, а просто удаляется) из списка на обратной стороне, затем соответствующая строка удаляется из базы данных также, как обычно (orphanRemoval = true
) но строка просто обновляется, когда Employee
сущность (дочерний элемент) добавляется в список другого родителя после того, как он был удален из списка его родителя (миграция сущности).
Поставщик, по-видимому, достаточно умен, чтобы обнаружить миграцию детей от их родителей к другому родителю в качестве обновления.
Поведение может быть идентичным как в Hibernate (4.3.6 final), так и в EclipseLink (2.6.0), но на него нельзя полагаться, если это поведение, специфичное для провайдера (не переносимое). Я не могу найти ничего об этом поведении в спецификации JPA.
1 ответ
Это задокументировано в спецификации JPA.
Раздел 3.2.4 (выдержка):
Семантика операции сброса, примененной к сущности X, следующая:
- Если X является управляемым объектом, он синхронизируется с базой данных.
- Для всех объектов Y, на которые ссылается отношение из X, если отношение к Y было аннотировано значением элемента каскада cascade=PERSIST или cascade=ALL, операция постоянства применяется к Y
Раздел 3.2.2 (выдержка):
Семантика сохраняемой операции, применяемой к сущности X, следующая:
- Если X является удаленным объектом, он становится управляемым.
orphanRemoval
JPA Javadoc:
(Необязательно) Применять ли операцию удаления к объектам, которые были удалены из отношения, и каскадно удалять операцию для этих объектов.
orphanRemoval
Документы Hibernate:
Если объект удаляется из
@OneToMany
коллекция или связанный объект разыменовывается от@OneToOne
ассоциация, этот связанный объект может быть помечен для удаления, еслиorphanRemoval
установлен вtrue
,
Итак, вы удалите сотрудника E
из отдела D1
и добавить ее в отдел D2
,
Hibernate затем синхронизирует отдел D1
с базой данных, видит, что E
нет в списке сотрудников и отметок E
для удаления. Затем он синхронизируется D2
с базой данных и каскадами PERSIST
операция со списком работников (раздел 3.2.4). поскольку E
теперь находится в этом списке, каскад применяется к нему, и Hibernate отменяет планирование операции удаления (раздел 3.2.2).
Вы также можете посмотреть на этот вопрос.
"Что произойдет, если orphanRemoval
установлен в true
на обратной стороне таких отношений?
Вы уже установили его на обратной стороне (обратная сторона является той, которая объявляет mappedBy
). Если вы имеете в виду, что если бы он был установлен на другой стороне (@ManyToOne
в данном случае), то это не имеет смысла, и поэтому такого атрибута в @ManyToOne
а также @ManyToMany
,