28

私は実際、休止状態でのこの動作を完全には理解していませんでした。「親」と呼ばれるエンティティで @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 です

4

2 に答える 2

37

Hibernate は、すべてのデータベースの制約 (MySQL の一意の制約など) を認識したり、尊重したりしません。これは既知の問題であり、すぐに対処する予定はありません。

Hibernate には、フラッシュ中に発生する操作の順序が定義されています。

エンティティの削除は、常に挿入後に行われます。私が知っている唯一の答えは、制約を削除するか、追加のフラッシュを追加することです。

EDIT:ちなみに、定義された順序の理由は、ユーザーが何か順不同を行ったとしても、これが外部キー制約(彼らが気にする制約の1つ)に違反しないことを保証する唯一の方法だからです。

于 2013-07-01T19:15:00.923 に答える
28

将来の読者のために、この問題を解決する 1 つの方法は、遅延制約を使用することです。PostgreSQL と Oracle はそれらをサポートしており、おそらく他の RDBMS もサポートしています。Hibernate はトランザクション内のすべてのステートメントを発行し、遅延はトランザクションのコミット時にのみ制約が適用されることを保証します。たとえば、PostgreSQL では次のようになります。

ALTER TABLE company
    ADD CONSTRAINT name_unique UNIQUE (name) DEFERRABLE INITIALLY DEFERRED;

理想的ではありませんが、シンプルで効果的です。

于 2014-03-11T20:00:21.387 に答える