2015-08-02 7 views
1

обратная сторона (департамент):Требуется ли удалить объект из его обратной коллекции, когда ссылка на родителя уже установлена ​​в значение null в сущности при слиянии?

@OneToMany(mappedBy = "department", fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH}) 
private List<Employee> employeeList = new ArrayList<Employee>(0); 

Обладание сторона (Сотрудник):

@JoinColumn(name = "department_id", referencedColumnName = "department_id") 
@ManyToOne(fetch = FetchType.LAZY) 
private Department department; 

Способ присоединения к Employee объект, поставляемый клиентом, имеющий нулевое Department в нем:

public Employee update(Employee employee) { 
    Department department = employee.getDepartment(); 

    if (department == null) { 
     Employee managedEmployee = entityManager.find(Employee.class, employee.getEmployeeId()); 
     // Obtain the original Employee entity which may still have its Department intact. 

     if (managedEmployee == null) { 
      throw new EntityNotFoundException(); 
     } 

     Department managedDepartment = managedEmployee.getDepartment(); 

     if (managedDepartment != null) { 
      managedEmployee.getDepartment().getEmployeeList().remove(managedEmployee); 
      // Removing an Employee entity from the list on the inverse side, 
      // since it will no longer be pointing to Employee after Employee is merged. 
     } 

     return entityManager.merge(employee); 
    } 
} 

Поставляется Employee является отдельным объектом. Предположим, что Department является необязательным для Employee s, и, следовательно, имеется нулевой внешний ключ (таким образом, ON DELETE SET NULL указан в внутренней системе).

Нужно явно удалить Employee объект, как показано на update() выше способом из списка сотрудников на обратной стороне отношения (Department), когда входящий в комплект поставки Employee не содержит Department (потому что он уже установленным на нуль клиентом при редактировании объекта Employee) перед слиянием поставленного объекта Employee?

Я думаю, провайдер сохранит ссылку Employee в списке сотрудников на обратной стороне отношений, в противном случае болтается.

+0

Был этот [предыдущий вопрос] (http://stackoverflow.com/q/31769284/1391249) с тем же содержимым, но я случайно забыл удалить 'cascade = CascadeType.ALL' и' orphanRemoval = true' на обратная сторона отношения, изменяющая все определение вопроса. – Tiny

+0

Как вы относитесь к основной транзакции метода 'update'? Вы используете расширенное распространение контекста постоянства? Вы извлекаете и/или модифицируете экземпляры сущностей, которые имеют значение до и/или после вызова метода «update»? Как? – Guillermo

+0

Это транзакционный объект EntityManager в EJB (по умолчанию используется '@PersistenceContext (type = PersistenceContextType.TRANSACTION)'). '@PersistenceContext (type = PersistenceContextType.EXTENDED)' не применяется к сессионным компонентам без состояния. Сохранение состояния с использованием подробных сессионных битов состояния для такого рода операций CRUD абсолютно избыточно. EJB потребляются JSF, где по мере необходимости сущности изменяются в соответствии с бизнес-требованиями и повторно передаются в соответствующий EJB для их распространения в базовую базу данных. Я не уверен, как это связано. – Tiny

ответ

1

Приложение должно поддерживать обе стороны двунаправленных отношений для кеширования. Если вы не сохраните коллекцию, она будет не синхронизирована с базой данных, пока она не будет перезагружена.

Это может быть необязательно, но это будет функцией вашего провайдера и кэширования. Если отдел кэшируется (в текущем EntityManager или в кеше второго уровня), и список был извлечен, он не будет знать об этом изменении. В этом случае вам придется принудительно перезагрузить объект, используя опцию обновления или исключение кеша. Как правило, гораздо проще поддерживать список напрямую. Если вас беспокоит производительность, вы можете проверить, был ли список уже загружен перед вызовом remove, но это не всегда необходимо (отслеживание изменений EclipseLink может разрешить изменение без загрузки коллекции).

«ON DELETE SET NULL» находится за пределами JPA, и я не знаю, как сообщить провайдеру, что это установлено на основе отношений. Нет смысла указывать отношение Employee.department к null и слиянию, поскольку JPA затем попытается обновить внешний ключ до нуля, что не является необходимым. Я бы не рекомендовал его использовать, но если это необходимо, обновите/недействите Employee в кеше вместо обновления отношения к null.

+0

'Employee.department' будет устанавливаться' null' самим клиентом при обновлении 'Employee' (поскольку' Department' является необязательным для 'Employee' (достаточно странно) , клиент может выбрать/решить удалить «Департамент» от конкретного «Сотрудника»). Этот объект 'Employee' с' null' 'Department' повторно отправляется клиентом, который должен быть объединен JPA (с' нулевым '' Департаментом' в нем). Здесь «* Не существует большого смысла в установлении отношения« Employee.department »к нулевому и слиянию, поскольку JPA затем попытается обновить внешний ключ до нуля, что не является необходимым. *« – Tiny

+0

Комментарий объединения применим только к «ON DELETE SET NULL», который появляется только при удалении Департамента. Клиент, устанавливающий для employee.department значение null, но сохранение работоспособности отдела - это особый случай, который требует слияния обоих изменений с JPA. Его боль, поскольку вы не можете полагаться на настройки слияния каскада, так как ссылки больше не существуют – Chris

+0

Итак, в целом, требуется, и сущность должна быть удалена вручную из инверсной коллекции в данной ситуации? – Tiny

1

Поскольку сторона владельца ассоциации находится на объекте Employee, нет необходимости удалять сотрудника из сотрудников Department.

Вы можете удалить эту часть кода.

Также имейте в виду, что метод merge будет вызывать IllegalArgumentException, если сотрудник является удаленным экземпляром объекта. Так что, возможно, вы не можете просто удалить часть (ваш сотрудник) из вашего кода, или, может быть, вы можете поймать исключение, перебрасывающее EntityNotFoundException, но все это зависит от вашей логики кода.

Добавление Oficial источника

Цитата из JPA 2 спецификации (JSR-338) раздел Синхронизация с базой данных:

двунаправленного отношения между управляемыми объектами будут сохранены на основе ссылок, принадлежащих владеющим сторона отношений. Обязанностью разработчика сохранить ссылки в памяти, хранящиеся на стороне владельца, и те, которые находятся на обратной стороне , согласуются друг с другом, когда они меняют. В случае однонаправленных отношений «один к одному» и «один ко многим» ответственность разработчика заключается в том, чтобы гарантировать соответствие семантики отношений. Особенно важно обеспечить, чтобы изменения в обратной стороне отношений приводили к соответствующим обновлениям на стороне владельца, чтобы гарантировать, что изменения не будут потеряны при синхронизации с базой данных.

+0

Почему это не требуется, пока JPA требует явно удалить объект из своей обратной коллекции перед удалением объекта (т. Е. 'EntityManager.remove (entity)')? – Tiny

+0

Для этой части кода не требуется, поскольку ассоциация представлена ​​полем ('department_id') в таблице Employee. После внесения изменений (которые устанавливают некоторый элемент «department_id» в null) для извлечения коллекций сотрудников Департамента поставщик найдет «Служебные кортежи», у которых есть идентификатор конкретного департамента – Guillermo

+0

. Если обратная коллекция будет загружена лениво, то «Сущность сотрудника», чей «Отдел» 'устанавливается равным null, в то время как объединение объекта« Employee »не будет видно, когда эта коллекция сотрудников извлекается из обратной стороны отношения, потому что она должна быть загружена заново из базы данных. То же самое верно при удалении/удалении объекта - удаленный объект будет невидимым в своей лениво загруженной обратной коллекции, когда эта коллекция будет извлечена. Продолжение следует. – Tiny