私は実際、休止状態でのこの動作を完全には理解していませんでした。「親」と呼ばれるエンティティで @OneToMany 関係を使用しています。これには次のように注釈が付けられています。
@OneToMany(cascade = {CascadeType.ALL, CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, orphanRemoval = true)
@JoinColumn(name = "entity_id", insertable = true, updatable = true, nullable = false)
private List<Child> children;
今、私は1つのトランザクション内で次のことをしたい:
- 親エンティティを取得する
- 子のリストを反復処理する
- 子の 1 つを削除する
- 新しい子を挿入する
つまり、基本的には、子供の 1 人を完全に置き換えるだけです。
私がこの問題を理解している限り、次のようなことができるはずです: (これは問題を説明するための Java 疑似コードにすぎないことに注意してください)
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteAndAdd(Long parentId, Long childId) {
Parent parent = entityManager.find(parentId);
for (Iterator it = parent.children.iterator(); it.hasNext();) {
Child child = it.next();
if (child.id == childId) {
it.remove();
}
}
Child newChild = new Child();
parent.children.add(newChild);
}
ただし、新しい Child が古いものと同じ一意のキー値を持つ場合、これは失敗します。したがって、基本的には、新しいエンティティが永続化される前に、古い子エンティティが適切に削除されていないようです。
次のように、古い子の削除と新しい子の永続化の間に entityManager.flush() を追加すると:
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteAndAdd(Long parentId, Long childId) {
Parent parent = entityManager.find(parentId);
for (Iterator it = parent.children.iterator(); it.hasNext();) {
Child child = it.next();
if (child.id == childId) {
it.remove();
}
}
entityManager.flush();
Child newChild = new Child();
parent.children.add(newChild);
}
すべて正常に動作します。必要に応じて、新しい子が挿入される前に子が削除されます。
休止状態がDBに送信されるステートメントの順序を混同すると仮定したくないので、そうでない休止状態について想定している何かが他にあるに違いありません。最初の例が機能しないのに、後者の例が機能する理由は何ですか?
休止状態のバージョンは 3.5 です。DB は Mysql InnoDB です