Принимая очень простой пример отношения "один ко многим" (состояние страны ->).
Страна (обратная сторона):
@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().