tags:

views:

213

answers:

1

In his excellent blog post "Hibernate query cache considered harmful?" Alex Miller (of Terracotta Inc.) explains why using the query cache can be harmful to latency and scalability.

My question is: is it possible to write a 'get all' DAO method for a particular domain object type that uses the second level cache without having a query cache?

My usual form of code for such a method involves the query cache, e.g.:

public List<Foo> getAllFoo()
{
    return (List<Foo>) getHibernateTemplate().execute(new HibernateCallback()
    {
        public Object doInHibernate(Session session)
        {
            Query q = session.createQuery("from Foo");
            // Cache the results in the query cache.
            q.setCacheable(true);
            return q.list();
        }
    });
}

My only vague thought is maintaining a cached collection of all Foo on some singleton domain object (which is also cached). Is there a more elegant way?

+1  A: 

If you're using EhCacheProvider, what you want is access to the CacheManager member variable. Unfortunately, it's private with no public access methods so I ended up creating my own copy of EhCacheProvider that used a static for CacheManager with a public static method to return it. I assume you could handle other cache providers the same way or just implements the CacheProvider interface.

Once you have the CacheManager, you get the Cache for the domain (class) name using cacheManager.getCache(class name). That returns a Cache which you can iterate through using cache.getKeys(). Each key is a CacheKey which has the entity id on it so you can then use cache.get(key) to return the actual entity.

Another option if you don't mind bypassing some Java security and depending on a specific Hibernate implementation is something like (exception handling omitted):

  EhCacheProvider cp = (EhCacheProvider)((SessionFactoryImpl)session.getSessionFactory()).getSettings().getCacheProvider()
  Field f = EhCacheProvider.class.getDeclaredField("manager");
  f.setAccessible(true);
  CacheManager cm = (CacheManager)f.get(cp);
  Cache c = cm.get(Entity.class);
Brian Deterling
Interesting. With accessing the cache directly like this, would I be responsible for checking whether the cache has been invalidated (e.g. due to another record being inserted) and refreshing the cache if it has? Or would I even have to take care of the invalidation myself by checking the timestamps cache?
David Easley
I don't think so. Calling the Cache.get method is just like doing a normal Hibernate load that hits the cache so it checks the expiration date. All this code is doing is getting the list of keys in the cache ahead of time and not hitting the database if it's not in cache. You'd want to do a null check after the get in case it expired between the time you got the list of keys and the time you got the element.
Brian Deterling