JPA, обновляющая двунаправленную ассоциацию
Предположим, что мы имеем следующие Сущности:
@Entity
public class Department {
@OneToMany(mappedBy="department")
private List<Employee> employees;
}
@Entity
public class Employee {
@ManyToOne
private Department department
}
В обновлении понятно, что нам необходимо поддерживать обе стороны отношения следующим образом:
Employee emp = new Employee();
Department dep = new Department();
emp.setDepartment(dep);
dep.getEmployees().add(emp);
Все до сих пор. Вопрос в том, следует ли применять слияние с обеих сторон следующим образом, а я избегаю второго слияния с каскадом?
entityManager.merge(emp);
entityManager.merge(dep);
Или сливается с собственной стороной? Также должны ли эти слияния произойти внутри транзакции или EJB? Или сделать это по простому методу контроллера с отдельными объектами достаточно?
Ответы
Ответ 1
Вопрос в том, следует ли применять слияние с обеих сторон следующим образом, а я избегаю второго слияния с каскадом?
Вы можете использовать элемент аннотации каскада для распространения эффекта операции на связанные объекты. Каскадная функциональность наиболее часто используется в отношениях родитель-потомок.
Операция merge
каскадируется для объектов, на которые ссылаются отношения из Department
, если эти отношения были аннотированы с помощью аннотации элемента cascade
cascade=MERGE
или cascade=ALL
.
Двунаправленные отношения между управляемыми объектами будут сохраняться на основе ссылок, принадлежащих стороне-стороне (Employee)
отношения. Ответственность разработчиков заключается в том, чтобы сохранить в памяти ссылки, хранящиеся на стороне пользователя (Employee)
, и те, которые находятся на обратной стороне (Department)
, согласованы друг с другом, когда они меняются. Таким образом, с помощью ниже ряда операторов связь будет синхронизирована с базой данных с помощью одного merge
:
Employee emp = new Employee();
Department dep = new Department();
emp.setDepartment(dep);
dep.getEmployees().add(emp);
...
entityManager.merge(dep);
Эти изменения будут переданы в базу данных при совершении транзакции. Состояние в памяти в сущности может быть синхронизировано с базой данных в другое время, когда транзакция активна с помощью метода EntityManager # flush.
Ответ 2
У вас есть операция persist (сделана с помощью em.merge()
). Сохранение нового сотрудника не означает, что отдел также сохраняется (у вас нет каскадирования), поэтому он будет генерировать исключение по другой причине (просто попробуйте и опубликуйте исключение). Чтобы этого избежать, вы либо добавляете каскадный тип, или сохраняйте их оба (как вы сделали в своем примере).
О вашем вопросе: единственная рассматриваемая часть будет принадлежать стороне.
В спецификации JPA 2.0 Chapter 3 Entity Operations
= > 3.2.4 Syncrhonization to the Database
следует следующее:
Двунаправленные отношения между управляемыми объектами будут сохраняться основанные на ссылках, принадлежащих владелической стороне отношений. это ответственность разработчиков за сохранение содержащихся в памяти ссылок на стороне обладателя и тех, которые находятся на обратной стороне, согласующейся с друг друга, когда они меняются. В случае однонаправленного одностороннего и отношения "один ко многим", это ответственность разработчиков чтобы гарантировать соответствие семантики отношений. [29]
Связано с необходимостью транзакции: да, для операции слияния требуется активная транзакция. Выдержка из спецификации JPA 2:
Методы persist, merge, remove и refresh должны быть вызваны внутри контекст транзакции, когда менеджер объекта с контекст постоянной транзакции. Если нет контекста трансакции, javax.persistence.TransactionRequiredException выбрано.
О том, как запущена транзакция/как вы начинаете транзакцию: она зависит от типа EntityManager
(как вы его получите). В EJB гораздо проще справиться с этим в общих ситуациях. Кроме того, согласно документации метода слияния генерируется исключение TransactionRequiredException, if invoked on a container-managed entity manager of type PersistenceContextType.TRANSACTION and there is no transaction
.