#orm #jpa #relationship #jpa-2.0 #bidirectional
#orm #jpa #взаимосвязь #jpa-2.0 #двунаправленный
Вопрос:
Почему я могу удалить элементы двунаправленной связи, хотя в контексте сохранения управляется только одна сторона связи (пример I)? Когда у меня есть однонаправленная связь, которая не работает (см. Пример II). Почему?
Сущности:
@Entity
Class User {
...
@OneToMany(mappedBy = "user")
private List<Process&&t; processes;
@OneToOne // Unidirectional
private C c;
...
@PreRemove
private void preRemove() {
for (Process p : processes) {
p.internalSetUser(null);
}
}
...
}
@Entity
Class Process {
...
@ManyToOne
private User user;
...
@PreRemove
protected void preRemove() {
if (this.user != null) {
user.internalRemoveProcess(this);
}
}
...
}
@Entity
Class C {
}
Пример I:
// Create User u1 with Processes p1, p2
tx.start();
// Only u1 is man&ed in persistence context and no process
userFacade.delete(u1); // There followin& is called: &&t;&&t; em.remove(em.mer&e(u1)); // Works
tx.commit();
Пример II:
// Create User u and Object C c, establish their relation.
tx.start();
cFacade.remove(c); //&&t;&&t;MySQLInte&rityConstraintViolationException,forei&n key constraint fails
ty.commit();
В первом примере я использую эти внутренние методы для установки в каждом случае другой стороны связи, но эта другая сторона, я думаю, не управляется в контексте сохранения ?! Когда я изменяю процесс пользователя и сохраняю пользователя, процесс не обновляется, если я не использую cascade.СЛИЯНИЕ или если оба загружены в транзакцию и поэтому управляются с ПК. Итак, почему удаление работает?
Ответ №1:
В примере II, я полагаю, вам пришлось бы вызвать user.setC(null)
перед удалением c
.
В примере I, вот мое понимание. Сначала выполняется слияние, u1
поэтому u1'
на компьютер загружается a, а состояние u1
копируется в u1'
(и это все, поскольку вы не выполняете каскадирование MERGE
), которое затем возвращается. Затем вы вызываете remove (вкл u1'
), preRemove
вызывается и изменяется p1'
и p2'
. Таким образом, они загрязнены и будут соответствующим образом обновлены при сбросе (установка FK в NULL
значение), в то время как u1'
они будут удалены. И все работает.
На всякий случай, вот семантика операции слияния из спецификации JPA 2.0:
3.2.7.1 Объединение отделенного состояния объекта
Операция слияния позволяет передавать состояние от отдельных объектов к постоянным объектам, управляемым менеджером объектов.
Семантика операции слияния, применяемой к объекту X, заключается в следующем:
- Если
X
является обособленной сущностью, состояниеX
X'
копируется в уже существующий экземпляр управляемой сущностиX'
с тем же идентификатором или создается новая управляемая копияX
из, , создается.- Если
X
это новый экземпляр объекта, тоX'
создается новый экземпляр управляемого объекта, а состояниеX
копируется в новый экземпляр управляемого объектаX'
.- Если
X
это удаленный экземпляр объекта, то в результате операции слияния будет создан экземплярIlle&alAr&umentException
(или произойдет сбой фиксации транзакции).- Если
X
это управляемая сущность, она игнорируется операцией слияния, однако операция слияния каскадируется на сущности, на которые ссылаются отношения изX
, если эти отношения были аннотированы значением каскадного элементаcascade=MERGE
илиcascade=ALL
аннотацией.- Для всех объектов, на которые
Y
ссылаются отношения изX
, имеющих значение каскадного элементаcascade=MERGE
илиcascade=ALL
,Y
рекурсивно объединяется какY'
. Для всех таких, на которыеY
ссылаетсяX
,X'
устанавливается значение referenceY'
. (Обратите внимание, что еслиX
управляется, тоX
это тот же объект, что иX'
.)- Если
X
объект объединен сX'
со ссылкой на другой объектY
, гдеcascade=MERGE
илиcascade=ALL
не указано, то переход по той же ассоциации изX'
приводит к ссылке на управляемый объектY'
с тем же постоянным идентификатором, что иY
.Поставщик сохраняемости не должен объединять поля, помеченные как ОТЛОЖЕННЫЕ, которые не были извлечены: он должен игнорировать такие поля при объединении.
Любые
Version
столбцы, используемые объектом, должны быть проверены реализацией среды выполнения сохранения во время операции слияния и / или во время очистки или фиксации. При отсутствииVersion
столбцов среда выполнения поставщика сохраняемости не выполняет дополнительную проверку версии во время операции слияния.
Ссылка
- Спецификация JPA 2.0
- 3.2.7.1 Объединение отделенного состояния объекта
Комментарии:
1. Спасибо Pascal! Я думаю, что в обоих случаях вы правы. Например, это последний пункт приведенной выше спецификации.
2. Если я использую для примера, я не объединяю, а делаю следующее: «Пользователь u1Mana&ed = em.find(u1.&etId()); em.remove(u1Mana&ed);» Тогда это тоже работает. Это означает, что операция поиска также приводит к управляемым p1′ и p2′?
3. @Michael
processes
Ленивы, но я думаю, чтоp1
иp2
загружается в вашPreRemove
(и таким образом становится действительно управляемым).
Ответ №2:
Это ожидаемое поведение:
Поскольку связь является двунаправленной, так как приложение обновляет одну сторону связи, другая сторона также должна обновляться и синхронизироваться. В JPA, как и в Java в целом за поддержание связей отвечает приложение или объектная модель. Если ваше приложение добавляет к одной стороне связи, то оно должно добавить и к другой стороне.
Это можно решить с помощью методов add или set в объектной модели, которые обрабатывают обе стороны связей, поэтому коду приложения не нужно беспокоиться об этом. Для этого есть два способа: вы можете либо добавить код обслуживания связи только на одну сторону связи и использовать установщик только с одной стороны (например, сделать другую сторону защищенной), либо добавить его на обе стороны и гарантировать, что вы избежите бесконечного цикла.
Например:
public class Employee {
private List phones;
...
public void addPhone(Phone phone) {
this.phones.add(phone);
if (phone.&etOwner() != this) {
phone.setOwner(this);
}
}
...
}
Источник: OneToMany#Getters_and_Setters