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

StackOverflow https://stackoverflow.com/questions/4725785

  •  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;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top