EntityManager.merge()
は,新しいオブジェクトを挿入したり,既存のオブジェクトを更新したりすることができます。
なぜpersist()
(新しいオブジェクトを作成することしかできない)を使いたいのでしょうか?
どちらの方法でもPersistenceContextにエンティティを追加しますが、違いはその後のエンティティの処理にあります。
Persistは、エンティティのインスタンスを取得し、コンテキストに追加し、そのインスタンスを管理します(つまり、将来のエンティティの更新が追跡されます)。
Merge は、エンティティの新しいインスタンスを作成し、渡されたエンティティから状態をコピーし、新しいコピーを管理するようにします。あなたが渡したインスタンスは管理されません(あなたが行った変更はトランザクションの一部ではありません-あなたが再度 merge を呼び出さない限り)。
コード例が参考になるかもしれません。
MyEntity e = new MyEntity();
// scenario 1
// tran starts
em.persist(e);
e.setSomeField(someValue);
// tran ends, and the row for someField is updated in the database
// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue);
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)
// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue);
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)
シナリオ1とシナリオ3はほぼ同等ですが、シナリオ2を使いたい場面もあるでしょう。
セッション中のレイジーロードされたコレクションにアクセスしようとしていたため、エンティティでレイジーロードの例外が発生していました。
私は別のリクエストで、セッションからエンティティを取得し、その後jspページでコレクションにアクセスしようとしましたが、これは問題がありました。
この問題を解決するために、コントローラで同じエンティティを更新し、それをjspに渡しました。ただし、セッションで保存しなおしたときには、SessionScope
からもアクセスできるようになり、LazyLoadingException
を投げることはないと想像していますが、例2の修正です。
私の場合は以下のようになりました。
// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"
//access e from jsp and it will work dandy!!