JPAの永続コンテキストの概念と、それがEntityManager#merge(Object)
メソッドによってどのように使用されるかについて、根本的な誤解があると思います。
バックグラウンド
動機
merge()
私は、突然起こっていないかもしれないことに気付いた操作の特定の動作に依存できるようにしたいと考えています。具体的には、ぼんやりと暗示されている機能に依存したいと思いUPSERT
ます。この質問の目的のために、カスケード動作やあらゆる種類のキャッシングには興味がありません。さらに、この質問では、あるベンダーがどのように物事を行うか、または別のベンダーがどのように物事を行うかには興味がありません。言語と要件に興味があります。
この質問は長く、意図的に何度か言い直されています。誰もが同じように頭を抱えているわけではないことがわかったので、さまざまな方法で提示したいと思いました. 冗長なことをお許しください。
仕様の引用
JPA 2.0仕様では、セクション7.1で次のように述べています。
永続コンテキストは、永続エンティティ ID に対して一意のエンティティ インスタンスが存在する管理対象エンティティ インスタンスのセットです[強調]。永続化コンテキスト内では、エンティティ インスタンスとそのライフサイクルはエンティティ マネージャーによって管理されます。
(したがって、このパラグラフは次のように述べています。一連のオブジェクトを考えてみましょう。それを永続コンテキストと名付けましょう。そのメンバーは、によって管理されるライフサイクルを持ち、データベースの土地で一致する永続表現に対応します。つまり、EntityManager
それらの は一致します。@Id
データベース内の対応する主キー)。
このパラグラフでは、オブジェクトがセットに入る方法 (エンティティが永続化コンテキストに入る方法) については述べていません。オブジェクトがそこにある場合に、どの条件が満たされなければならないかだけを示していることに注意してください。物事がセットに入る方法に関する規則は、仕様の他の場所で定められています。
最後に、完全を期すために、空の永続コンテキストについて話すことは意味的に問題ないように思われますEntityManager#clear()
。
次に、セクション 3.2.7.1 は次のように述べています。
エンティティ X に適用されるマージ操作のセマンティクスは次のとおりです。
• X が切り離されたエンティティである場合、X の状態は同じ ID の既存の管理エンティティ インスタンス X' にコピーされるか、X の新しい管理コピー X' が作成されます。
質問
永続化コンテキストで既存のマネージド エンティティ インスタンスについて話すとき、正確には、既存とはどういう意味ですか?
または: 回りくどい方法で、私は本当に何をするのかについて尋ねてclear()
います: それは本当に永続化コンテキストを空にしますか、それとも私が現在参照を保持している可能性のあるオブジェクトだけを空にしますか?
または: 永続化コンテキストが空であるとします (呼び出したばかりですEntityManager#clear()
)。アイデンティティの「既存の管理対象エンティティ インスタンス」はまったくありますか? すべてのデータベース行は、永続コンテキストに本質的に存在するようなものですか?つまり、操作clear()
は、永続コンテキストが本当に空であることを意味するのではなく、何かを追加しないと何も取得できないということだけですか?
または:merge()
操作の実装において、永続化コンテキストが空ではなかったかのように、X' をジャストインタイムで検索、インスタンス化、および管理しようとすることが、この段落によって義務付けられEntityManager
ている実装です。時間は次のように呼ばれました。merge()
merge()
// Assuming a previously empty persistence context (em.clear()).
// Pseudocode follows as though this were part of the
// EntityManager merge() implementation.
//
// Here "pre-existing" means "not there to start with,
// but located just in time and thus snuck into the
// persistence context from disk as though it had been
// there all along". This would mean that things like
// primary key violations and the like at flush() time would be
// effectively prohibited by spec.
SomeEntity xPrime = this.find(SomeEntity.class, x.getID());
if (xPrime == null) {
xPrime = new SomeEntity();
manage(xPrime);
}
assert isManaged(xPrime); // hypothetical method
copyProperties(x, xPrime);
return xPrime;
この場合、呼び出された時点で永続化コンテキストが空だったX'
としても、既存であると言われていますか?merge()
または、代わりに、適合者EntityManager
は単純に次のことを行うことができます。
// Assuming a previously empty persistence context.
// Pseudocode follows as though it were part of the
// EntityManager merge() implementation.
//
// Here "pre-existing" means "already in the persistence context",
// which given that we're assuming we started with an empty one,
// means we just do a new instance here. This scares me but I can't
// see how the spec rules this out.
SomeEntity xPrime = new SomeEntity();
copyProperties(x, xPrime);
manage(xPrime);
assert isManaged(xPrime); // hypothetical method
return xPrime;
この解釈の正当化は、「...または X の新しいマネージド コピー X' が作成される」という文の一部であり、JPA ベンダーがfind()
最初にやりたいかどうかを決定できると思います。この 2 番目の解釈では、nofind()
が暗黙的に試行flush()
されるため、主キー違反が発生する可能性があるように見えます。JPA プロバイダーは、実際に発生INSERT
するはずの場所で、UPDATE
これは、同じ質問をより具体的にしたものです。
永続化コンテキストが空であるとします (たとえば、 を呼び出しEntityManager#clear()
ました)。
値 を含むアノテーション付きフィールドを持つ分離されたエンティティ を手に持っているとX
します。 戻ります。@Id
long
6L
EntityManager#contains(x)
false
x
データベースに、主キーが である行を含むテーブルが6
あり、さらにX
エンティティがそれにマップされているとします。
(私のオチを早めに言うとX'
、この時点で はマネージド エンティティ インスタンスとして事前に存在していると言えますか? 即座にジャストインタイムで管理できるため、暗黙的に「事前に存在する」のでしょうか。が存在しますか? を呼び出したことを思い出してくださいem.clear()
)。
EntityManager#merge(Object)
ここで、分離されたエンティティ ( )を呼び出すとしX
ます。
仕様によると、「X の状態は、同じ ID の既存のマネージド エンティティ インスタンス X' にコピーされるか、X の新しいマネージド コピー X' が作成されます」。
6
永続化コンテキストに「同じ ID の既存のマネージド エンティティ インスタンス X」(この場合) はありますか? を呼び出したことを思い出してくださいem.clear()
。
ある解釈は次のように述べています。いいえ、もちろんありません。永続化コンテキストが空です。私たちはclear()
それを編集しました。そこには何もありません。そのため、EntityManager
文の 2 番目の部分 (「...または、X の新しいマネージド コピー X' が作成されます」) にフォールバックできます。
別の言い方: はい、あります。このEntityManager
場合、 は my の識別子を取得するために仕様で必要とされ、X
それを使用して操作と同等の操作をすばやく試行します。実行されると、実際の操作を効果的に開始する前に常にそこにあったかのように、永続コンテキストにfind()
静かに配置されます。操作のセマンティクス。X'
merge()
どちらの解釈が正しいですか? 後者を希望します。読んでくれてありがとう。