Saturday, February 19, 2011

No need to check that now, check it later...

Let's say you have an Oracle DB containing a table A with a foreign key to a table B and we want to add a foreign key constraint to ensure referential integrity. This might look something like this:

ADD CONSTRAINT "A_FK_B_CONSTRAINT_NAME" FOREIGN KEY ("COL_IN_A_REFFING_B") REFERENCES "TABLE_B" ("PK_ID")

Now if your A object has a oneToMany collection of B objects and you cascade delete these B objects whenever you remove A, you might run into a problem in the way the constraint is defined now. Hibernate deletes the A and B rows in 2 steps or statements, within the same transaction. If Hibernate first updates the PK_ID to NULL, COL_IN_A_REFFING_B will reference a non-existing row, which will cause an exception from the FK constraint checker.
This is why you need to add 'DEFERRABLE INITIALLY DEFERRED' to the constraint:

ADD CONSTRAINT "A_FK_B_CONSTRAINT_NAME" FOREIGN KEY ("COL_IN_A_REFFING_B") REFERENCES "TABLE_B" ("PK_ID") DEFERRABLE INITIALLY DEFERRED

This allows the constraint checking to be deferred or postponed till commit time. A constraint that is not deferrable (or DEFERRABLE INITIALLY IMMEDIATE) will be checked immediately after the execution of every statement. Currently only FK constraints accept this clause, other constraints are not deferrable. A good 'chicken or egg' post can be found here.