次のように定義された2つのエンティティBeanがあります(無関係なものは削除されています):
@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 Set にも指示どおりにデータがロードされます。次に、このデータを UI に送信し、更新されたコピーを取得して保持します。ユーザーは MasterItem オブジェクトのフィールドを更新しますが、CriticalItems セットやその中の何かには触れず、変更されないままです。
私の save() メソッドが呼び出されると、Hibernate は Set of CriticalItems 内の各アイテムの SQL 更新を送信することを主張しますが、それらのどれもまったく変更されていません。
掘り下げた後、これが私が起こっていると思うことです。saveOrUpdate() を実行すると、Hibernate は MasterItem オブジェクトが切り離された状態にあることを認識し、ディスクからリロードしようとします。ただし、そうすると、準備済みステートメント (起動時に Hibernate によって自動作成されたもの) を使用しているように見え、この準備済みステートメントは CriticalItems データへの結合を試みません。
したがって、Hibernate には CriticalItems の完全な Set を持つ更新された MasterItem オブジェクトがありますが、コレクションのない MasterItem を「previousState」オブジェクトとして使用します。したがって、すべての CriticalItems は SQL 経由で更新されます (挿入されません。これ自体が興味深いことです)。
この動作を引き起こす注釈で何かをしましたか? Interceptor を使用してアイテムが実際に変更されたことを確認したり、ダーティ フラグを変更して Hibernate のデフォルト アルゴリズムをオーバーライドしたりできることはわかっていますが、これは Hibernate が私の介入なしに独自に処理する必要があるようです。
任意の洞察をいただければ幸いです。
更新: コメントに基づいて、saveOrUpdate() と merge() の違いを理解していると思います。saveOrUpdate() は、すべての場合に SQL INSERT または SQL UPDATE のいずれかになり、理論的には、オブジェクトが永続的な状態から変更された場合にのみマージが更新を発行することを認識していますが、それを判断するために、Hibernate最初に SQL SELECT を介してオブジェクトをリロードする必要があります。
そのため、コードに戻って saveOrUpdate() を 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
それを修正すると、例外はなくなりました。