Friday, June 17, 2011

Blog Moved to Tumblr!

This blog now lives on Tumblr. Thanks for visiting!

http://jeffnadler.tumblr.com/

Friday, March 11, 2011

Hibernate Collection Cache and Cascades

When using Hibernate collection caching, there are some problems that come up that require incredibly awkward code workarounds. I'm writing this post in the hopes that someone has come up with something more elegant.

When you delete an entity, you need to write the code to remove that entity from all c0llections in which it might be cached. Hibernate doesn't do this automagically. They say:

When I delete an object that belongs to a cached collection, the collection cache is not invalidated!

You must remove the deleted object from all collections it belongs to.

(source: http://community.jboss.org/wiki/HibernateFAQ-AdvancedProblems#When_I_delete_an_object_that_belongs_to_a_cached_collection_the_collection_cache_is_not_invalidated)


Now that's not ideal, because it makes my code very prone to error. Later, if I fail to maintain the collection properly and try to iterate it's members, I will get an EntityNotFoundException.


Fine, in my DAO I'll write teh code to look up the other entities that refer to this entity and remove this entity from their cached collections. Done, right?


Wrong. Now consider cascading deletes. Because Hibernate handles the cascade, entities are being deleted indirectly (not via any direct calls to my DAO). Those entities that are cascade-deleted may be members of many other collections caches, and they need to be removed.

Maybe we'll try @PreRemove, which runs for each entity that is cascade-deleted, and there we can look up the other entities that cache this entity and remove this entity from the collection. Right?

Wrong. This can cause ConcurrentModificationException in cases where the collection.remove(entity) that is done in @PreRemove conflicts with the cascade operation. The @PreRemove seems to run first, is successful, then deep in the bowels of Hibernate the CME is thrown when the cascade operation hits the same entity. Note that we have a hierarchical data model (entity has a collection of children of the same type, deletes cascade down the tree) and that this condition may be specific to that case.

Not much we can do here. We're thinking of writing our own cascade-delete logic to handle this case; this will allow us to maintain the collection caches as we go.

Argh. Any ideas?