Thursday, February 5, 2009

Understanding Hibernate Delete Orphan issues

Ever wonder how Hibernate knows to follow the annotation "CascadeType.DELETE_ORPHAN"?

The short answer is that Hibernate keeps track of removals from these collections. When you remove an object from the collection, the status of that object is set to Status.DELETED or Status.GONE if the object is still in the persistence context. When hibernate saves the object tree that contained these ex-collection objects, it sees that the object has been dereferenced, marked for deletion, and is then removed from persistent storage and the persistence context.

Thats how it is supposed to happen.

Ever wonder why you are getting the error :
org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity ?

Lets say that you have an object with a collection that is cascade type delete orphan, and it has some child entities in it. And lets say that you change this collection simply by calling the setter on the parent object:

parent.setChildren(someDifferentSet);

or

parent.setChildren(null);

The children that were in the collection are still in the persistent context. However, they are no longer referenced by the parent object, and Hibernate never said goodbye to them. In the Collections class (org.hibernate.engine.Collections) , the method processDereferencedCollection(Collection, Session) throws a hibernate exception when it finds these items in the persistence context, and their Status is not set to GONE or DELETED. The ensuing error message: 'A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity: ' followed by the parent entity name is shown.

The proper way to handle this situation where you want to replace an existing collection would be:

parent.getChildren().clear();
parent.getChildren().addAll(someDifferentSet);

Similiarly, if you just wanted to remove a child from the collection:

parent.getChildren().remove(child);

By calling the collections manipulations, hibernate is kept up to date with the status of the objects within and without the collection. It then manages the objects in the persistence session properly.



2 comments:

Tarale Seena said...

I have been bitten by this bug and cant seem to find a way.
Your solution, does work for some simple cases.
I have a situation where I have to load a collection, based on a customized query, with criteria and order passed.

Criteria query = session.createCriteria();
query.addOrder(blah);
query.addCriteria(blah blah);
List colln = query.list();

I get this exception now and for the life of me cannot figure a way out!

Bob Hedlund said...

More than likely the issue you are having is due to one of the following:

1. Your annotations are not quite right.

2. Your HashCode / equals implementation is nt correct, and Collection API calls fail

3. You are trying to manipulate the collections before objects are persistent.