Остановите Hibernate обновлять коллекции, когда они не изменились
-
12-10-2019 - |
Вопрос
У меня есть две сущности, определяемые следующим образом (не связанные материалы):
@Entity
@Table(...)
public class MasterItem implements java.io.Serializable {
private Set<CriticalItems> criticalItemses = new HashSet<CriticalItems>(0);
@OneToMany(fetch = FetchType.EAGER, mappedBy = "masterItem", orphanRemoval = true,
cascade = {javax.persistence.CascadeType.DETACH})
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
public Set<CriticalItems> getCriticalItemses() {
return this.criticalItemses;
}
}
CriticalItems определяется следующим образом:
@Entity
@Table(...)
public class CriticalItems implements java.io.Serializable {
private MasterItem masterItem;
@ManyToOne(fetch = FetchType.LAZY, optional = false,
cascade = {javax.persistence.CascadeType.DETACH})
@Cascade({CascadeType.SAVE_UPDATE})
@JoinColumn(name = "mi_item_id", nullable = false)
public MasterItem getMasterItem() {
return this.masterItem;
}
}
И в моем коде DAO - у меня есть эти методы:
public MasterItem load(int id) {
MasterItem results = (MasterItem) getSessionFactory().getCurrentSession()
.get("com.xxx.MasterItem", id);
}
public void save(MasterItem master) {
// master has been changed by the UI since it
getSessionFactory().getCurrentSession().saveOrUpdate(master);
}
Когда я загружаю Masteritem, он загружается правильно, а также загружает набор CriticalItems с помощью данных, как указано. Затем я отправляю эти данные в свой пользовательский интерфейс и получаю обновленную копию, которую я затем стараюсь настойчиво. Пользователи обновляют поля в объекте Masteritem, но не касается набора CriticalItems или чего -либо в нем - он остается неизменным.
Когда мой метод сохранения () вызывается, Hibernate настаивает на отправке обновлений SQL для каждого элемента в наборе CriticalItems, даже если ни один из них не изменился каким -либо образом.
После некоторого копания, вот что я думаю, происходит. Когда я делаю SaveTorUpdate (), Hibernate видит, что мой объект Masteritem находится в отдельном состоянии, поэтому он пытается перезагрузить его с диска. Однако при этом он, по-видимому, использует подготовленное заявление (которое было автоматически создано Hibernate при запуске), и это подготовленное утверждение не пытается присоединиться к данным CriticalItems.
Таким образом, Hibernate имеет мой обновленный объект Masteritem с полным набором критических символов, но использует Masteritem без коллекций в качестве объекта «предыдущие штаты». Таким образом, все критические символы обновляются с помощью SQL (не вставлены, что само по себе интересно).
Я сделал что -то в своих аннотациях, что вызвало такое поведение? Я знаю, что могу использовать перехватчик, чтобы узнать из элемента, действительно изменился, или изменил грязный флаг, чтобы переопределить алгоритм по умолчанию Hibernate - но, похоже, это то, что Hibernate должен просто обращаться сами по себе без моего вмешательства.
Любое понимание будет оценено.
ОБНОВИТЬ:Основываясь на комментариях, я думаю, что понимаю разницу между SavtorUpdate () и Merge (). Я понимаю, что SaveTorUpDate () приведет либо к вставке SQL, либо во всех случаях SQL -обновления, и что слияние теоретически выпускает обновления только в том случае, если объект изменился из его постоянного состояния, но для того, чтобы определить это, спятить должен первым перезагрузить объект через SQL Select.
Итак, я подумал, что смогу просто вернуться в свой код и изменить SaftorUpdate (), чтобы Merge (), и это сработало, но это было не совсем так.
Когда я использовал Merge (), я получал
org.springframework.orm.hibernate3.HibernateSystemException: could not initialize proxy - no Session; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session
Но это сработало нормально, если я вернулся на SaveTorUpDate ().
Я наконец узнал, почему - я не включил CascadeType.MERGE
в моем @Cascade
Аннотация (тьфу). Как только я исправил это, исключение исчезло.
Решение
Это семантическая разница между update()
а также merge()
.
Из Кристиан Бауэр и Джава Кристиан Кинга (Я не могу найти четкое объяснение такого поведения в документах с зимством):
Метод Update () вызывает обновление в постоянное состояние объекта в базе данных, всегда планируя обновление SQL.
...
Не имеет значения, изменяется ли объект элемента до или после его передачи в Update ().
...
Hibernate всегда рассматривает объект как грязный и планирует обновление SQL, которое будет выполнено во время промывки.
С другой стороны, merge()
Сначала запрашивает базу данных и не выполняет обновление, если состояние не изменилось.
Итак, если вы хотите, чтобы Hibernate сначала запрашивал базу данных, вам нужно использовать merge()
(Хотя поведение по умолчанию update()
можно переопределить, определяя @org.hibernate.annotations.Entity(selectBeforeUpdate = true)
на ваших сущностях).
Другие советы
Попробуйте добавить столбец пересмотра (оптимистичная блокировка) в свои сущности
@Version
Date lastModified;