I found an interesting quirk in Hibernate and figured there'll be many around like me trying to look for solutions. The used case here is to delete rows from the child table through a cascade effect on the parent object.
The solution here is to use cascade="all-delete-orphan" on the parent config of the one-to-many relationship. If you don't set the cascade option correctly, Hibernate will try to update the removed child rows with null values for the mentioned foreign key and not delete them from the table. This will probably result in DB exceptions as the FK might be a part of the PK.
After you've set the cascade option, you code can be something like the sample Java code mentioned below:
Set<Child> children = parent.getChildren();
for(Child child : children) {
if(child.getBoolProperty()) {
children.remove(child);
} else if(child.getIntProperty() == 1) {
child.setIntProperty(2);
}
}
Child newChild = new Child(prop1, prop2);
children.add(newChild);
parent.save();
The remove() operation will delete the row when the save() is called, the setProperty() method will result in a updation and the add() will insert. Point to note also is that if the Set already contains an object with the given PK, you should be updating that object and not adding a new one to the set. This makes intuitive sense but is a common error all newbies encounter. Any new object added to the set has to have a different PK when compared to the other objects in the set. Otherwise, you’ll end up with this exception:
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session
I haven't tried what happens if you remove the existing object from the Set and add a new one with the same PK as the one you removed. I think the row will be removed and another one inserted but that's not very efficient, so I don't see why you'd wanna go down that route :)
Addendum : If you do use cascade="all-delete-orphan", you have to remember to give Hibernate complete ownership of the collection. This means you you should never be creating your own Collection and setting it to the session, instead you should add and remove objects from the collection Hibernate has already instantiated.
This means that the following is incorrect:
Set<Child> children = new HashSet<Child>();
children.add(child);
parent.setChildren(children);
What you should be doing is the following:
Set<Child> children = parent.getChildren();
children.add(child);
A pretty good explanation of the error is here. If you make this mistake, you'll end up with this exception:
org.springframework.orm.hibernate3.HibernateSystemException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance
Note to self : Modify css template to ensure code snippets are readable.
23 hours ago

3 comments:
Hi
This saved me some trouble. Thanks a bunch!
Mike
You're welcome, Mike!
Thank you, you hit the nerve here with your awesome might. You saved my week!
Raikka
Post a Comment