Wednesday, April 26, 2006

Hibernate Evictions

[Preface: We use Hibernate 2.1.8, Java 5, Tomcat 5.5.x]

Yesterday, Dusty and I were working on doing some new things (for us) with hibernate. Our objective was to take a subset of order objects, "clean" them up using code that had a bug fixed in it, and then persist them back.

The main "new" thing we were doing is trying to do this whole operation with a single session. The problem however, is we were using a thread local construct that we'd learned in the basic hibernate tutorials, but some of the code we were calling grabbed the thread local hibernate session and then closed it. It turned out there were quite a few places that this could potentially happen, so we had to go through and temporarily break the code (for normal operation) by making it not close (I have an idea for this that I'll post about later). So once we "fixed" all the session closing code, we got an error that basically told us there were two copies of our object we were trying to save in the session. This error occurred when it attempted to flush.

So we started looking through the code and tried to figure out where we were getting this duplication (the duplication actually told us that there were two copies of a Map relationship within our object). I had attended the Advanced Hibernate session at NFJS by Justin Gehtland, and I remembered a little bit about the eviction concept, and the level 1 cache that is in the session. I started trying to investigate whether there was a way to iterate through all the objects in the session just for debugging purposes. This led us to the iterate() method, which we didn't know how to use, so we tried to look this up in the API, and we found that it worked basically like find(), but returned an iterator instead of a list. Somehow I got it into my head that it would only go and get the stuff already in the session, but after a good 4 minutes of the code running and doing nothing, I inserted some debugging and learned that we were just uselessly pulling all the order objects up from the table only to immediately evict them from the session.

Finally, we figured out the real problem. Somewhere in our bug fixed code, we were loading all the orders that were placed by the same person to check some information. This secondary query was happening after the original object had been "dirtied" by other changes, so there had to be two different copies of the object in the session.

One solution to this problem was as follows:

long id = ...
Order dirty = session.get(Order.class, id);
session.evict(dirty);
dirty.codeThatMakesItDirtyAndGetsAnotherCleanCopy();
Order clean = session.get(Order.class, id);
session.evict(clean);
session.update(dirty);
session.flush();

I doubt this is the best or most efficient way to do this, but it did in fact work for our problem. Another option is to modify the code so that it doesn't get a separate clean copy of itself in the same session. This probably would have been possible, but we didn't want to modify "production" code just for this cleanup operation.

Labels: , , , ,

0 Comments:

Post a Comment

Links to this post:

Create a Link

<< Home