4

エンティティ A および B での循環参照を使用した次の Hibernate 3.6 エンティティ マッピングを検討してください。

@MappedSuperclass
abstract class Entity {

  @Id
  protected UUID id = UUID.randomUUID();

  @Version
  protected Integer revision;
} 

@Entity
class A extends Entity {
  // not null in the database
  @OneToOne(optional = false)
  B b;
}

@Entity
class B extends Entity {
  // not null in the database
  @ManyToOne(optional = false)
  A a;
}

エンティティの ID は、新しいインスタンスの作成時に生成されるため、SQL INSERT の前に設定されます。インスタンスが一時的かどうかを判断するには、次のようにしますInterceptor

class EntityInterceptor extends EmptyInterceptor {

  @Override
  public boolean isTransient(Object entity) {
    return ((Entity)entity).getRevision == null;
  }
}

(単一のトランザクションで) A と B のインスタンスを (相互に参照を設定して) 保存しようとすると、Hibernate はTransientObjectException(オブジェクトは保存されていない一時的なインスタンスを参照します - フラッシュする前に一時的なインスタンスを保存します) で失敗します。

A a = new A();
B b = new B();
a.setB(b);
b.setA(a);

// start transaction
sessionFactory.getCurrentSession().saveOrUpdate(a); // a before b or vice versa doesn't matter
sessionFactory.getCurrentSession().saveOrUpdate(b);
sessionFactory.getCurrentSession().flush();
// commit

マッピングをAb と Ba のカスケード保存に変更すると、Hibernate は A に対して次の SQL INSERT ステートメントを生成します。

INSERT INTO A (id, revision, b) VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 0, NULL);

NOT NULLこれはAb の制約に違反し、を引き起こしますConstraintViolationException。B の ID は挿入時にわかっていますが、SQL INSERT では設定されません。データベース (PostgreSQL 9.1) では、Ab に対する FK 制約が定義されDEFERRABLE INITIALLY DEFERREDているため、Ab が設定されている場合、次の INSERT ステートメントはエラーなしで実行されます。

START TRANSACTION;
INSERT INTO A (id, revision, b) VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 0, 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb');
INSERT INTO B (id, revision, a) VALUES ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 0, 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa');
COMMIT;

データベースの Ab に NOT NULL 制約をドロップし、カスケード保存マッピングを保持すると、上記のコードで A と B を保存すると正常に動作します。 編集PostgreSQL では制約を延期することはできません 。FKNOT NULL制約のみを延期できます。 END EDIT 正直に言うと、この場合に生成されたSQLステートメントを見ていませんでした(そして今は再現できません)が、次のようになると思います:

START TRANSACTION;
INSERT INTO A (id, revision, b) VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 0, NULL);
INSERT INTO B (id, revision, a) VALUES ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 0, 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa');
UPDATE A SET b = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' WHERE id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa';
COMMIT;

そもそもここでやろうとしていることに対して、より良いエンティティ設計があるかもしれないことは知っていますが、NOT NULL制約を維持する方法があるかどうかを本当に知りたいです (可能であれば、カスケード保存なしの元のマッピングも) ) を作成し、元のコードを機能させます。B が A の挿入時に一時的であっても、Hibernate に Ab = B.id で A を挿入するように指示する方法はありますか? 一般に、Hibernate および deferred FK 制約に関するドキュメントは見つかりませんでした。

4

1 に答える 1

1

ここにあるのは、古典的な双方向の関係です。両側は互いに参照しています。双方向の片側を強い側としてマークする必要がある場合、これは、mappedBy または inverse を使用して芯側をマークすることによって行われます。さらに、FK 制約があるため、Hibernate が FK を挿入するには、not-null が true であることを定義する必要があります。

完全なこれを見てください。

于 2012-08-04T16:25:19.670 に答える