Java – Can’t get hibernate to delete children instances via cascade, that were removed from a one-to-many relation

all-delete-orphancascadehibernatejavaone-to-many

slight problem here:

I have two entity classes, let's say

class Parent {
    Set<Child> children;
}
class Child {
    SomethingElse reference;
}

now the mapping is essentially:

<class name="Parent" lazy="false">
    <set name="children" lazy="false" cascade="all-delete-orphan">
        <key column="parent_id"/>
        <one-to-many class="Child"/>
    </set>
</class>

(i omitted the id mappings and fields here, i use regular generated ids)

i essentially need to keep a clean database as in when i remove elements from a parent's list and then commit the parent, the according removed child database entries need to be deleted. The Child instances reference other entities that i need to be able to delete later, so if the child instance remains in the database i cannot delete those referenced objects.

What i have found out so far: any of the things i try below should work if i were to keep hibernate's PersistentCollection wrapper in place. The issue is, my database objects come through a few layers of frameworks, which include a UI framework, that uses bean property abstractions to invoke setters, and a network communication layer that clones and serializes the objects back and forth. Both of these layers internally replace the collection instances and thus remove these PersistentCollection wrappers. Rewriting these to not do so is not an option.

That said, there's 8 things i tried that did not work:

1) configure the relation as cascade="all", use session.update(parent).

2) configure the relation as cascade="all-delete-orphan", use session.update(parent).

3) configure the relation as cascade="all" and use session.merge(parent)

All of these result in hibernate executing a "UPDATE CHILD SET parent.id = null WHERE parent.id = …". This succeeds in removing the children from the parent list when reloading the parent instance, but the child instance remains in the database and prevents me from deleting the other referenced entities.

4-6) using configuration 1-3 while additionally having the parent key column defined as non-null

This results in hibernate not doing anything. I read in another post that making the key column non-null would cause the deletion. Sounded possible since updating to null is no longer an option, but doesn't work. If i remove children from the collection, commit the change and reload the instance from the database, the children re-appear.

7+8) parent key nullable or non-null doesn't matter, but configure the relation as cascade=all-delete-orphans and use session.merge(parent)

This causes hibernate to throw an exception "A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance", due to the removed PersistentCollection wrapper.

To solve my problem, the only thing that i need is hibernate to execute the query from options 1-3 as a DELETE instead of an UPDATE. I hope i am just unable to find the option to configure the mapping in a way that deletes these without the PersistentCollection wrappers, but to me currently it appears like there is no such option. Does anyone know if there is a way to configure this?

/edit: To clarify, example of what i want to happen:

Parent parent = new Parent();
parent.setChildren(new HashSet<Child>(Arrays.asList(new Child()))));
session.insert(parent)
// this correctly results in (approximately):
// SQL> INSERT INTO PARENT ...
// SQL> INSERT INTO CHILD ...

parent.setChildren(new HashSet<Child>()); // using .clear() is not an option.
session.update(parent);
// this results in:
// SQL> UPDATE CHILD set parent_id = null WHERE parent_id = ${id.of.parent}
// but i need this to result in:
// SQL> DELETE FROM CHILD WHERE parent_id = ${id.of.parent}

Best Answer

Okay i apparently fixed it now. The issue was i was not assigning an empty set, but null. Apparently, in the case of session.merge(updated), hibernate suddenly differentiates between empty collections and null collections. Using cascade="all-delete-orphan" and .merge() with empty collection instances assigned to the properties works, assigning null instead of an empty collection instance throws the mentioned exception. This is the same regardless of nullability constraints on the key column.

I don't know if that is considered intentional behavior as usually null values act the same way as empty collections. I'll see if i can find out some more about this and then maybe put up a bug report.

update: issue at https://hibernate.atlassian.net/browse/HHH-7726

Related Topic