Hibernate ‘on-delete=cascade’ Performance Tuning
Be careful when you use Hibernate’s support for database ON DELETE CASCADE constraint. If not configured properly, your application might be more performance-costly than you think.
Let me use the example from Gavin King’s email introducing the new setting of on-delete=”cascade”:
<set name="children" inverse="true" cascade="all"> <key name="PARENT_ID" on-delete="cascade"> <one-to-many class="Child"> <set>
For a parent object associated with N child objects (cascade=”all”), the setting on-delete=”cascade” avoids issuing N deletes to the database if the parent were to be deleted.
Gavin also tells us that this setting only optimizes away the delete statements while all semantics are preserved. Well, what exactly does he mean?
I will roughly explain the procedure Hibernate performs to cascade delete in the simplest scenario:
- mark entity for deletion
- iterate through all of entity’s associations having cascade delete (one or many)
- if the association is single, perform delete on this association (by starting step 1 for this association)
- if the association is many, perform delete on this collection by:
- iterate all the collection elements, loading them from the database if necessary
- for each collection element, perform delete on the element (by starting step1 for this element)
- if not on-delete=”cascade”, issue delete statement on this entity
When you have a to-many association with cascade=”all” as described in our example, Hibernate iterates the associated collection having size N, loading them from database if necessary, and marks each collection element for deletion. Before actually issuing delete statements for these elements, Hibernate searches for any other cascades configured on them and perform these found cascading actions accordingly.
Even with the setting on-delete=”cascade” on a specific lazy collection, cascade=”all” causes Hibernate to initialize and iterate the collection (because it wants to check if the elements in this collection also cascade delete to other associations). Therefore, when you are sure there are no other cascading actions configured on your child entity (the element in the on-delete=”cascade” collection), use the setting cascade=”save-update” instead of cascade=”all” to prevent Hibernate from performing the delete cascade checks and consequently avoid loading your lazy to-many association into memory.
If you have multiple layers of delete cascade, from parent to children and from child to grand children, consider the size of your collections and decide if you want to set up multiple layers of ON DELETE CASCADE constraints in database accordingly or preserve the configuration of cascade=”all” and on-delete=”cascade” to let Hibernate handle the delete cascade on the grand children.