Reattaching an object having no modification

Posted by    |      

It happens sometimes that a domain model objet dettached from a previous session needs/can be reattached without trigging an UPDATE (whether it has optimistic locking or not). Hibernate supports this kind of feature by providing:

session.lock(myEntity, LockMode.NONE);

Thus, Hibernate will not propagate the modifications unless they are made after the reattachment to the session.

Don't use it as an optimization

First of all, try to avoid this feature. Usually, reattaching an object using session.saveOrUpdate() or by retrieving it through a session.get()/session.load() is fast enough for you. I'm not kidding, do not suppose it's significantly faster in your real application not to update the object (esp. when versioning is switched on) or not to retrieve it from the DB: do some benchmark before any decision.

Beware of boundaries

Associated object are not always managed the way you want. Uncascaded properties are not reattached to the session.

I am currently working on a project where some entities needs to be reattached using a lock mode NONE. In my case, the entity is a Contact. Contact is linked to some barely updated (and cached) entities. In a particular but very frequent use case, I know for sure that the Contact instance I get is the same as the DB version one. It appeared to me to be the best use case for session.lock(contact, LockMode.NONE);

Unfortunatly, the contact I retrieve is associated (but not cascaded) to some lazy loaded (at the second level of the graph) entities, so I cannot properly walk through my object graph.

The solution I've used is to redefine properly my object graph boundary, by refreshing some of the Contact properties with fresh entities.

contact = ... //get it from somewhere
session = sf.openSession();
Transaction tx = session.beginTransaction();
ContactType type = session.load( 
        ContactType.class, 
        contact.getContactType().getId() 
    );
contact.setContactType(type);
session.lock(contact, LockMode.NONE);
//work with contact safely

I'm now able to walk my graph through contact, eg:

contact.getContactType().getFavoriteChannel();

Notice several things:

  • I used load() instead of get(), it allows me to get a proxy if available and thus don't hit the DB
  • I set the refreshed property before reattaching the object to avoid useless updates.

Know what you do

This feature it quite complex because it may break the ACID semantic (contact and the other entities where not loaded in the same transaction) and may lead to very tricky issues. I did use this optimization (reduce of DB hit) because I knew my application business process very well, I was sure it won't break any data, and because it makes me gain signficant time. I have considered seriously the following solution.

session.get(Contact.class, contact.getId());

This one line of code is your friend, don't ban it too early. That's the most natural way of playing with the object.


Back to top