Принимая очень простой пример отношения "один ко многим" (состояние страны ->
).
Страна (обратная сторона):
@OneToMany(mappedBy = "country", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<StateTable> stateTableList=new ArrayList<StateTable>(0);
StateTable (владеющая сторона):
@JoinColumn(name = "country_id", referencedColumnName = "country_id")
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
private Country country;
Метод, пытающийся обновить предоставленный (отсоединенный) объект StateTable
в активной транзакции базы данных (JTA или локальный ресурс):
public StateTable update(StateTable stateTable) {
// Getting the original state entity from the database.
StateTable oldState = entityManager.find(StateTable.class, stateTable.getStateId());
// Get hold of the original country (with countryId = 67, for example).
Country oldCountry = oldState.getCountry();
// Getting a new country entity (with countryId = 68) supplied by the client application which is responsible for modifying the StateTable entity.
// Country has been changed from 67 to 68 in the StateTable entity using for example, a drop-down list.
Country newCountry = entityManager.find(Country.class, stateTable.getCountry().getCountryId());
// Attaching a managed instance to StateTable.
stateTable.setCountry(newCountry);
// Check whether the supplied country and the original country entities are equal.
// (Both not null and not equal - http://stackoverflow.com/a/31761967/1391249)
if (ObjectUtils.notEquals(newCountry, oldCountry)) {
// Remove the state entity from the inverse collection held by the original country entity.
oldCountry.remove(oldState);
// Add the state entity to the inverse collection held by the newly supplied country entity
newCountry.add(stateTable);
}
return entityManager.merge(stateTable);
}
Следует отметить, что для параметра orphanRemoval
установлено значение true
. Объект StateTable
предоставляется клиентским приложением, которое заинтересовано в изменении ассоциации сущностей Country
(countryId = 67
) в StateTable
на что-то еще (countryId = 68
) (таким образом, на обратной стороне в JPA, дочерний объект из своего родителя (коллекции) другому родителю (коллекции), который orphanRemoval=true
будет в свою очередь против).
Поставщик Hibernate выдает оператор DELETE
DML, заставляющий строку, соответствующую объекту StateTable
, удаляться из таблицы базы данных.
Несмотря на то, что для параметра orphanRemoval
установлено значение true
, я ожидаю, что Hibernate опубликует регулярное выражение UPDATE
DML, в результате которого действие orphanRemoval
будет приостановлено полностью, потому что ссылка отношения перенесена (не просто удалено).
EclipseLink выполняет именно эту работу. Он выдает оператор UPDATE
в приведенном сценарии (с тем же отношением с orphanRemoval
, установленным на true
).
Какой из них ведет себя в соответствии со спецификацией? Возможно ли сделать Hibernate выдачей оператора UPDATE
в этом случае, кроме удаления orphanRemoval
с обратной стороны?
Это только попытка сделать двунаправленную связь более последовательной на обеих сторонах.
Методы управления защитными связями, а именно add()
и remove()
, используемые в приведенном выше фрагменте, если необходимо, определены в объекте Country
следующим образом.
public void add(StateTable stateTable) {
List<StateTable> newStateTableList = getStateTableList();
if (!newStateTableList.contains(stateTable)) {
newStateTableList.add(stateTable);
}
if (stateTable.getCountry() != this) {
stateTable.setCountry(this);
}
}
public void remove(StateTable stateTable) {
List<StateTable> newStateTableList = getStateTableList();
if (newStateTableList.contains(stateTable)) {
newStateTableList.remove(stateTable);
}
}
суб >
Обновление:
Спящий режим может выдавать только ожидаемый оператор UPDATE
DML, если указанный код изменяется следующим образом.
public StateTable update(StateTable stateTable) {
StateTable oldState = entityManager.find(StateTable.class, stateTable.getStateId());
Country oldCountry = oldState.getCountry();
// DELETE is issued, if getReference() is replaced by find().
Country newCountry = entityManager.getReference(Country.class, stateTable.getCountry().getCountryId());
// The following line is never expected as Country is already retrieved
// and assigned to oldCountry above.
// Thus, oldState.getCountry() is no longer an uninitialized proxy.
oldState.getCountry().hashCode(); // DELETE is issued, if removed.
stateTable.setCountry(newCountry);
if (ObjectUtils.notEquals(newCountry, oldCountry)) {
oldCountry.remove(oldState);
newCountry.add(stateTable);
}
return entityManager.merge(stateTable);
}
Соблюдайте следующие две строки в более новой версии кода.
// Previously it was EntityManager#find()
Country newCountry = entityManager.getReference(Country.class, stateTable.getCountry().getCountryId());
// Previously it was absent.
oldState.getCountry().hashCode();
Если последняя строка отсутствует или EntityManager#getReference()
заменяется на EntityManager#find()
, то неожиданно выдается a DELETE
выражение DML.
Итак, что здесь происходит? Особенно я подчеркиваю мобильность. Не переносить эту базовую функциональность между различными поставщиками JPA сильно нарушает использование структур ORM.
Я понимаю основное различие между EntityManager#getReference()
и EntityManager#find()
.