Monday, November 22, 2010

Caching Hibernate Proxied Objects

Generally, caching objects that have loaded from database helps to improve performance when retrieved several times. However, there is something to be aware of when lazily loading objects from hibernate and putting them into the cache store.

If you're like me, and you're using spring's OpenSessionInViewFilter and a cache store to store objects, you might encounter the dreaded LazyInitializationException.

Here's why:

Say you access the same object over several web requests:

Request #1: 
 1) OpenSessionInViewFilter opens a new session
 2) You load the object from hibernate but never actually retrieve it's lazily loaded proxied associations. 
 3) The object along with its proxed associations, gets stored in the cache
 4) OpenSessionInViewFilter closes the session. The proxy object no longer has it's session opened.


Request #2:
1) OpenSessionInViewFilter opens a new session
2) You attempt to load the same object from the cache. A match is found and the cached object is returned.
3) You attempt to load associated objects that are proxied.


Step 3, is where it fails with LazyInitializationException because, the session was already closed.


Solution:


Reattach the cached object to the session.


    @Override
    public void reattach(T entity) {
        Session session = getSession();
        try {
            if (PoddObject.class.isAssignableFrom(entity.getClass())) {
                PoddObject po = (PoddObject) entity;
                Long id = po.getId();
                if (id != null) {
                    try {
                        session.buildLockRequest(LockOptions.NONE).lock(entity);                    } catch (NonUniqueObjectException e) {
                        LOGGER.info("Merging entity instead of locking");
                        session.merge(entity);
                    }
                }
            } 
        } catch(HibernateException e) {            
            LOGGER.warn(e);
        } finally {
            releaseSession(session);            
        }
        
    }


1 comment:

  1. PoddObject.class.isAssignableFrom(entity.getClass())

    should be written as

    entity instanceof PoddObject

    ReplyDelete