У меня есть две сущности, определенные следующим образом (несвязанные вещи удалены):
@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 должен обрабатывать сам по себе без моего вмешательства.
Любое понимание будет оценено.
UPDATE:
Основываясь на комментариях, я думаю, что понимаю разницу между 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
аннотацию (тьфу). Как только я это исправил, исключение ушло.