考虑以下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约束的文档,所以任何关于它的指针都会受到赞赏。
答案 0 :(得分:1)
这里有你的经典bidirectional relationship。双方都互相参考。 如果你有双向的,那么一方需要被标记为更强的一方,这是通过使用mappedBy或反向来标记灯芯方面来完成的。另外你有FK约束,所以需要定义not-null为真,以便hibernate插入FK。
查看complete。