【问题标题】:Hibernate 3.6: how to make use of deferred FK constraints when inserting entities with circular reference?Hibernate 3.6:插入具有循环引用的实体时如何利用延迟的 FK 约束?
【发布时间】:2012-07-01 07:09:22
【问题描述】:

考虑以下 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.b = B.id 插入 A,即使 B 在插入 A 时是瞬态的? 一般来说,我找不到任何关于 Hibernate 和延迟 FK 约束的文档,所以任何关于它的指针都值得赞赏。

【问题讨论】:

  • 听起来很多this related case of the chicken-egg-problem。该答案的第二部分可能在这里有用。
  • 此外,我们发现 here 中定义的约束行为的 PostgreSQL 文档目前缺乏。
  • @ErwinBrandstetter:(一如既往)对鸡蛋问题的一个很好的回答!我当然没有为我的问题的数据库部分而苦苦挣扎,但我还没有想到 back-reference 外键成语。真的很好,当我的 As 和 B 上线时,它肯定会在那里 ;) 我不知道 SQLAlchemy,也许我需要相当于“我已经使用 select_id 上的 use_alter 和 SystemVariables 选择上的 post_update 解决了循环关系关系”对于我的问题的休眠部分。也许 SQLAlchemy 专家可以告诉我这是否也涵盖了我的非空问题...
  • 很遗憾,我既不是 Hiberante 也不是 SA 专家。你需要别人帮忙。
  • 我在 Oracle 11gR2 上遇到了非常相似的问题。见here

标签: hibernate postgresql foreign-keys hibernate-mapping


【解决方案1】:

您在这里拥有的是经典的bidirectional relationship。双方相互参照。 如果你有双向的一侧需要标记为更强的一侧,这可以通过使用mappedBy 或反向标记灯芯侧来完成。此外,您还有 FK 约束,因此需要定义 not-null 为 true,以便 hibernate 使用 FK 插入。

查看complete

【讨论】:

    猜你喜欢
    • 2017-06-13
    • 1970-01-01
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 2019-01-02
    • 1970-01-01
    • 1970-01-01
    • 2020-04-10
    相关资源
    最近更新 更多