2015-06-22 1 views
5

Ниже приведено соотношение «один-ко-многим» от Department до Employee.Объединение управляемого объекта на стороне @ManyToOne

отдела (родитель):

@OneToMany(mappedBy = "department", fetch = FetchType.LAZY, cascade = CascadeType.ALL) 
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; 

Объединение управляемого объекта (ребенок), как показано ниже (с использованием СМТ в EJB),

Employee employee = entityManager.find(Employee.class, 1L); 
employee.setDepartment(department); // department is supplied by a client. 
employee.setEmployeeName("xyz"); 
entityManager.merge(employee); 

не обновляет соответствующую строку сотрудника в таблице базы данных. Это происходит только тогда, когда CascadeType.MERGE удаляется с ребенка @ManyToOne в Employee.

Почему обновляется строка в таблице? Какова единственная цель CascadeType.MERGE в отношении этого примера?

Я в настоящее время использую EclipseLink 2.6.0 с JPA 2.1.

+0

Я думаю, что это ошибка в EclipseLink. Вы пробовали другие версии EclipseLink или Hibernate? Назначение 'CascadeType.MERGE' в ​​этом примере - это, как правило, инициировать также' CascadeType.MERGE' в ​​поле 'department'. –

+0

Даже удаление этой строки 'entityManager.merge (employee);' должно обновлять строку в базе данных, поскольку это управляемый объект.Однако удаление этой строки приводит к тому, что весь список сотрудников, связанных с 'Department' (' employee.setDepartment (department); '), повторно вводится повторно, если только' CascadeType.PERSIST' не удален из '@ ManyToOne' в' Employee '. ** Это отлично подходит для Hibernate. ** Поэтому в EclipseLink это ошибка. – Tiny

+0

В любом случае я создал проблему на [Bugzilla] (https://bugs.eclipse.org/bugs/show_bug.cgi?id=470697). – Tiny

ответ

2

Каскадирование должно всегда propagate from a Parent to a Child, а не наоборот.

В вашем случае, вы должны удалить каскад из дочернего стороны:

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

и убедитесь, что вы установили обе стороны ассоциации:

Employee employee = entityManager.find(Employee.class, 1L); 
employee.setDepartment(department); 
employee.setEmployeeName("xyz"); 
department.getEmployeeList().add(employee); 
entityManager.merge(department); 
1

Этот код является рискованным, поскольку вы связываете экземпляр отдельного отдела, который, предположительно, имеет отдельный коллектив сотрудников. Если ваш текущий сотрудник с новым именем xyz находится в этом списке, его изменения будут переопределены именем отдельного экземпляра.

, например, после вызова employee.setDepartment (department); сотрудник (1L) -> отдел '-> сотрудник (1L)'

Вызов слияния на экземпляре сотрудника (1L) ничего не сделает, поскольку изменение имени уже видно, но оно будет входить в отдел. Затем объединяющий отдел каскадирует экземпляр сотрудника (1L), который имеет старое имя. Если вы проверили значение employee.getEmployeeName(), вы увидите, что слияние вызвало его сброс, что, вероятно, не означает, что вы не видите обновление базы данных.

Не вызывать слияние, хотя это не вариант, потому что у вас все еще есть сотрудник, ссылающийся на отдельный отдел, который должен быть случай исключения. Вот почему провайдер выпускает вставки.

Предположительно у вас есть каскадное слияние, установленное в отношениях, потому что объект отдела, предоставленный клиентом, также может содержать изменения сотрудников. Если это так, чтобы синхронизировать эти изменения и изменения employeeName, вы будете использовать:

Department managedDepartment = entityManager.merge(department); 
Employee employee = entityManager.find(Employee.class, 1L); 
employee.setDepartment(managedDepartment); 
managedDepartment.getEmployeeList().add(employee); 
employee.setEmployeeName("xyz"); 

Это и может добавить сотрудника в отдел, если он не существует, и до сих пор делают изменение имени, если оно есть.

+0

Спасибо, связка, Крис. Мне удалось получить техническую ошибку в коде. – Tiny

+0

'employee.setDepartment (department);' должен быть 'employee.setDepartment (managedDepartment);'. Думаю, это должна быть типографская ошибка. – Tiny

1

Я прочитал другие ответы и проблему с bugzilla, но я все же уверен, что это поведение является ошибкой или, по крайней мере, отсутствующей функцией.

Пожалуйста, следуйте моим мыслям:

Employee employee = entityManager.find(Employee.class, 1L); 

работник является управляемым объектом;

employee.setDepartment(department); // department is supplied by a client. 

отдел является отдельным объектом;

entityManager.merge(employee); 

Поскольку работник уже управляется, слияние сотрудника вызовет только слияние каскада на отдел.
Итак:

merge(department) 

Теперь depatment управляется тоже; и это вызовет каскад на сотрудников:

merge(employee1) 
merge(employee2) 
... 
merge(employeeN) 

но есть два различных сценария:

  1. department.getEmployeeList является уже принес
    он содержит отдельные лица: слияние будет крепят сотрудника # и не должен триггерный каскад на кафедре, так как он уже вызывается (в противном случае генерируется бесконечный цикл).
    Обратите внимание, что сотрудник не включен в employeeList.
    В этом случае все должно работать.
  2. department.getEmployeeList является еще не неправдоподобным и лениво загружен Теперь
    он содержит управляемый объекты: слияние не должны делать ничего, так как сотрудников # уже удалось и каскад на кафедре уже называется.
    Но ..

В данном случае это сотрудник, содержащийся в employeeList?

Три возможности (в зависимости от других переменных, таких как флеш-режиме автоматической фиксации, ...):

  1. нет: все должно работать
  2. да, и это тот же самый пример: все должно работать
  3. да, но это другой пример: вероятно, приводит к изменениям перезаписать

Я думаю, что «ошибка» может быть здесь, когда ленивая загрузка: это не должно быть possibl e, чтобы иметь два управляемых экземпляра одного и того же объекта в одном EntityManager.

Я не могу представить ни одного контрпримера.

+1

Независимо от того, является ли это ошибкой или нет, но сообщество разработчиков программного обеспечения добавляет новые возможности достаточно давно, но это не мешает исправлению ошибок, если они есть :) – Tiny

+1

Вы правы, в частности, для EclipseLink. Может быть, когда-нибудь он рухнет в черной дыре, проглотив все наши приложения ... –

Смежные вопросы