Thursday, April 19, 2007

Where is "delete-orphans" ?

A Customer has a shopping cart with OrderItems in it:

public class Customer {
    ...
    @OneToMany(cascade=CascadeType.ALL)
    private Set<OrderItem> cart;
    ...
}


What happens if one removes an OrderItem from the cart ? As I noticed today, the item no longer belongs to the customer 's cart but unfortunately still remains in the database as an orphan record.

Ok, let's set the cascade type to all-delete-orphans (like in hibernate) so that orphans get automagically deleted. Not so easy, though. JPA (JSR-220) does not support the "delete-orphan" feature of hibernate and other persistence providers. What can one do? Either use a hibernate specific annotation (other persistence providers like TopLink have similar features) to declare the cascade as delete-orphan or delete the orphan entity programmaticaly (after removing it from the Customer 's collection).

Personally, I chose the second approach as the first ties the application with the specific persistence provider (hibernate in my case).

12 comments:

javapapo said...

I do the second as well. As you said no delete orphan. If a child 'dies' you have to programmatically remove the connection from the parent.

In complex db schemas with lots of graph connections, I agree this might be..a bit problematic..in terms of development and maintenance!

Anonymous said...

Yes I agree with you, the second approach is better in case when you would like to avoid dependencies to specific JPA implementations. But it's very problematic in some cases. For example if you are using merge operation and you do not know which entities have been removed from collection. It's why I personally use the first one.

Unknown said...

That case was something I hadn't thought about. As a matter of fact I have started using hibernate annotations in more cases lately

victori said...

Hey, I am in the same boat. Took me hours of googling to figure out why Cascade deletes were not working on child instances.I was really pissed at hibernate at first until I found out it was a JPA limitation. Ended up with the former solution since the latter forces me to create DAOs for the child pojos, which just creates extra unnecessary code. Furthermore, I don't think it is a big deal since annotations don't bind it to hibernate when compiled, it is just meta data afterall.

Unknown said...

@Victor:
Or you can use xml for the hibernate specific configuration and thus keep your source code completely independent.

Unknown said...

I am also in the same boat, I have tried a lot to make it delete its children when merging but it does'nt work. Is there any way so can I delete???????

Unknown said...

@adnanraza No straightforward way. You either delete "by hand" or use implementation specific annotations (e.g. hibernate)

kj said...

My database has a constraint saying that the foreign key (from child to parent) cannot be NULL. After I remove the child via hibernate by hand (which by the way removes the row from the DB), the next time I flush the entity manager I get a hibernate exception saying that the foreign key in my child table cannot be NULL. How can I avoid this exception? Any ideas?

Thanks,

Brian said...

another scenario: Suppose the Customer has many PhoneNumbers:
@OneToMany(cascade=CascadeType.ALL)
private Set phoneNumbers;

If you update one of the PhoneNumbers, and then call merge() the Customer, the old PhoneNumber remains in the database as an orphan, and the updated PhoneNumber is added to the database, linked to the Customer. Delete orphans handles this too. Because my app has this as a scenario, I prefer to add the annotation:

@OneToMany(...)
@org.hibernate.annotations.Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set phoneNumbers;

Unknown said...

JPA 2.0 now supports the "orphanRemoval=true" attribute in the @xxxToMany annotation, adding it does the trick

Anonymous said...

Have a look at this - http://jbossguru.com/archives/45

Unknown said...

@Anonymous It seems my post has been an inspiration for others!