Остановить Hibernate от обновления коллекций, если они не изменились

у меня есть два компонента сущности, определенные следующим образом (несвязанный материал удален):

@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 или что - либо в нем-он остается неизменным.

когда вызывается мой метод save (), Hibernate настаивает на отправке обновлений SQL для каждого элемента в наборе CriticalItems, даже если ни один из них не изменился каким-либо образом.

после некоторых раскопок, вот что я думаю, что происходит. Когда я делаю saveOrUpdate (), Hibernate видит, что мой объект MasterItem находится в отключенном состоянии, поэтому он пытается перезагрузить его с диска. Однако при этом он похоже, используется подготовленный оператор (который был автоматически создан Hibernate при запуске), и этот подготовленный оператор не пытается присоединиться к данным CriticalItems.

таким образом, Hibernate имеет мой обновленный объект MasterItem с полным набором CriticalItems, но использует MasterItem без коллекций в качестве объекта "previousState". Таким образом, все CriticalItems обновляются через SQL (не вставляются, что интересно само по себе).

Я сделал что-то в своей аннотации, что вызвал такое поведение? Я знаю, что могу использовать перехватчик, чтобы узнать, что элемент действительно изменился, или изменил грязный флаг, чтобы переопределить алгоритм по умолчанию Hibernate, но это, похоже, то, что Hibernate должен просто обрабатывать самостоятельно без моего вмешательства.

любое понимание будет оценили.

обновление: Основываясь на комментариях, я думаю, что понимаю разницу между saveOrUpdate() и merge(). Я понимаю, что saveOrUpdate () приведет во всех случаях SQL INSERT или SQL UPDATE, и это слияние, теоретически, будет выдавать обновления только в том случае, если объект изменился из его постоянного состояния, но для того, чтобы определить это, Hibernate должен сначала перезагрузить объект через SQL SELECT.

Итак, я думал, что могу просто вернуться в свой код и изменить saveOrUpdate() на 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

но он работал хорошо, если я вернусь к saveOrUpdate().

я, наконец, узнал, почему-я не включил CascadeType.MERGE в своем @Cascade аннотация (тьфу). Как только я это исправил, исключение исчезло.

2 ответов


в этом семантическая разница между update() и merge().

С Кристиан Бауэр и Гэвин Кинг Java настойчивость с Hibernate (Я не могу найти четкое объяснение этого поведения в документах Hibernate):

метод update() принудительно обновляет постоянное состояние объекта в база данных, всегда планирование обновления SQL.
...
Не имеет значения, изменяется ли объект item до или после он передается обновление.)(
...
Зимовать всегда обрабатывает объект как грязный и планирует обновление SQL., который будет выполнен во время промывания.

С другой стороны, merge() сначала запрашивает базу данных и не выполняет обновление, если состояние не изменилось.

Итак, если вы хотите, чтобы Hibernate сначала запросил базу данных, вам нужно использовать merge() (хотя поведение по умолчанию update() можно переопределить, указав @org.hibernate.annotations.Entity(selectBeforeUpdate = true) на ваш сущности.)


попробуйте добавить столбец ревизии (оптимистическая блокировка) к вашим сущностям

@Version
Date lastModified;