Consider the following Hibernate 3.6 entity mapping with a circular reference in objects A and B:
@MappedSuperclass abstract class Entity { @Id protected UUID id = UUID.randomUUID(); @Version protected Integer revision; } @Entity class A extends Entity {
An entity identifier is generated when a new instance is created, so it is set before any SQL INSERT. To determine if an instance is temporary or not using an Interceptor :
class EntityInterceptor extends EmptyInterceptor { @Override public boolean isTransient(Object entity) { return ((Entity)entity).getRevision == null; } }
When I try to save (in one transaction) instances of A and B (with links set to each other), Hibernate fails with a TransientObjectException error (the object refers to an unsaved transient instance - it saves a temporary instance before flushing).
A a = new A(); B b = new B(); a.setB(b); b.setA(a);
When I change the mapping to cascading Ab and Ba Hibernate generates the following SQL INSERT statement for A:
INSERT INTO A (id, revision, b) VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 0, NULL);
This violates the NOT NULL on Ab and raises a ConstraintViolationException . Although identifier B is known at insert time, it is not set to SQL INSERT. In the database (PostgreSQL 9.1), the FK restriction on Ab is defined by DEFERRABLE INITIALLY DEFERRED , so if Ab is set, the following INSERT statements will be executed without any errors:
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;
When I remove the NOT NULL constraint on Ab in the database and save the cascading save mapping, this code to save A and B works fine. EDIT The NOT NULL cannot be delayed in PostgreSQL, only the FK constraint can be delayed. END EDIT Honestly, I did not consider the generated SQL statements in this case (and I cannot reproduce it right now), but I assume that it looks like this:
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;
I know that there may be a better entity design for what I am trying to do here in the first place, but I would really like to know if there is a way to preserve the NOT NULL (and, if possible, also the original mapping without cascading saving) and make my source code work. Is there a way to tell Hibernate to just insert A with Ab = B.id, although B is temporary during insert A? As a rule, I could not find any documentation regarding Hibernate and pending FK restrictions, so any pointers to this were appreciated.