Установка 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,

Другие вопросы по тегам