JPA EntityManager。なぜ、merge()ではなくpersist()を使用するのですか?

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を使いたい場面もあるでしょう。

解説 (12)

em.mergeを使用すると、JPAが生成するフィールドがない場合でも、INSERTごとにSELECT文が表示されることに気づきました(主キーフィールドは自分で設定したUUIDでした)。 そこで、em.persist(myEntityObject)に切り替えたところ、INSERT`文だけが表示されるようになりました。

解説 (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!!
解説 (0)