How to live with hibernate proxies

Stumbled over the fucking hibernate-CGlib-Proxies again. So here is what I found out and what helped me a lot:

If you load an object via the load()-method hibernate returns a proxy by default. As long as you don’t access any primitive property this thing won’t materialize. No prob, you might think and in most circumstances it isn’t. But consider this example:

class Plan {

// Cascade-type all, fetch lazy
private Plan lastVersion;
private boolean active

public Plan(Plan lastVersion){
if (lastVersion != null){
lastVersion.active = false;
}
this.active = true;
}
}

I don’t want to expose a public setter here to ensure the integrity of my versioning. Now the fuck-up:

Let’s say I get the current Plan from my Client and want to save it, the following code seems pretty straight forward:

// transactionmanaged service-method

Plan lastVersion = getHibernateTemplate().load(Plan.class, 4)

Plan currentPlan = new Plan(lastVersion);

getHibernateTemplate().saveOrUpdate(currentPlan);

BUT (as you might have guessed) this doesn’t work. At the end I do have two entries in my db both marked as active. Why that? lastVersion is a cglib-Proxy and hibernate does not recognize any changes that are not done by the setter. A possible workaround would be a private setter in the plan class, but somehow not even this is recognized by hibernate. So what are our options? One is to change the fetchmode of the lastversion. But doing so means sucking all previous versions from db when loading. Same happens when you mark the class @Proxy(lazy=false) as its a recursive structure. Played around with access types a while but that doesn’t have any influence. Finally I found out, that if you work with the “real” object instead of the proxy, it all works fine. To do so, you have to use the method get() instead of load(). Get() makes the db call instantly and returns a materialized object. In difference to the eager-fetchmode, all containing subtypes remain proxies.

That all leads to the questions, how we can make a clean separation between the ORM-Layer and our business-objects with hibernate. It’s just not transparent, when we got hibernate-specific-behaving objects flyin’ around in our domain-layer. And well, I don’t really have an answer on that…

8 thoughts on “How to live with hibernate proxies”

  1. Why does hib not recognize private setter? Simple answer: The CGLib-Proxy cannot override this method.

  2. Another selfmade comment:
    All these problems lead to another BIG BIG problem: How do we test an application which uses hibernate? We did not find a really good answer yet. Our workaround is a H2-database. So we don’t mock the DAOs but write getPersistentBlaBla()-methods which write to DB and return the loaded proxy. Pain in the ass…..

  3. how about a package-access setter? What do you mean a private setter is not recognized by Hibernate – it behaves as if you used a direct field access?

  4. package access is a possibility, but I want a real encapsulation. Hibernate works with CGlib-Proxies. These proxies are dynamic subclasses: If you have a proxy, you have in fact a subclass. This subclass overwrites the methods of the parent class and initialisizes the “real” object when such a method is called. As you cannot override private methods, calls won’t be recognized.
    Field access is something else: That only defines how hibernate accesses the mapped objects.

  5. You guys almost hit the nail on the head, so I’d figure I’d awake this thread from the dead and finally put this issue to bed.
    private setters cannot be overridden or accessed by subclasses. This is your problem.
    package-private can be overridden and accessed by subclasses, but opens up your encapsulation. You don’t want this.
    There are only two more modifiers:
    public is obviously not what you want for the same reasons as package-private.
    The last one:
    protected is the perfect tool for the job here. It will allow subclasses, and only subclasses to access your fields. Fixes the hibernate issues and keeps your encapsulation in check.

    Encapsulamatron to the rescue.

Comments are closed.