Hibernate 3.6:如何在插入具有循环引用的实体时使用延迟的FK约束?

时间:2012-06-29 18:28:35

标签: hibernate postgresql foreign-keys hibernate-mapping

考虑以下Hibernate 3.6实体映射,在实体A和B中使用循环引用:

@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

当我将映射更改为A.b和B.a的级联保存时,Hibernate为A生成以下SQL INSERT语句:

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

这违反了A.b上的NOT NULL约束并导致ConstraintViolationException。 尽管在插入时已知B的id,但它未在SQL INSERT中设置。 在数据库(PostgreSQL 9.1)中,A.b上的FK约束被定义为DEFERRABLE INITIALLY DEFERRED,因此如果设置了A.b,则以下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;

当我在数据库中删除A.b上的NOT NULL约束并保持级联保存映射上面的代码以保存A和B时工作正常。 的修改 NOT NULL约束不能在PostgreSQL中延迟,只能推迟FK约束。 结束编辑 老实说,在这种情况下,我没有看一下生成的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约束(如果也可能原始映射没有级联保存)并使我的原始代码工作。 有没有办法告诉 Hibernate只是插入A与A.b = B.id,即使B在A的插入时间是瞬态的? 一般来说,我找不到任何关于Hibernate和延迟FK约束的文档,所以任何关于它的指针都会受到赞赏。

1 个答案:

答案 0 :(得分:1)

这里有你的经典bidirectional relationship。双方都互相参考。 如果你有双向的,那么一方需要被标记为更强的一方,这是通过使用mappedBy或反向来标记灯芯方面来完成的。另外你有FK约束,所以需要定义not-null为真,以便hibernate插入FK。

查看complete