Version 2

    Hibernate version 2.1.4 introduced the concept of a pluggable QueryCache by making net.sf.hibernate.cache.QueryCache an interface as opposed to a concrete class. The standard implementation (net.sf.hibernate.cache.StandardQueryCache) performs the same functionality present in previous versions of Hibernate; namely caching query results based on an "update timestamp" of the underlying tables. The results in this cache are invalidated once Hibernate recognizes that the underlying tables have changed. At that point it discards the results and requeries the database. Essentially, this defines a stale intolerant query cache.

     

    Normally, that is exactly the desired behavior; however in certain circumstances that may not be the case. Thus the ability to plug-in a different user-defined implementation was added. Reasons for wanting to do this may range from tables only being updated via external process which do not have access to the underlying cache implementation or extremely slow running queries which can tolerate a certain degree of staleness.

     

    The query cache is responsible for two main functions which implementers of custom plug-ins must be aware:

    • Reducing a result set of entities down into just their ids prior to placing in the cache;
    • Determining when a given result set has become stale.

     

    The StandardQueryCache is designed to be subclassed. It provides the basics of these responsibilities and provides a convenient, protected, and overridable isUpToDate() method for subclasses to be able to override in order to define custom invalidation semantics. Of course you are free to implement the QueryCache interface from scratch, if need be.

     

    As an example, below is an implementation which understands, through its configuration properties, that updates to certain tables should not invalidate queries based on those tables:

        public class StaleTolerantQueryCache extends StandardQueryCache {
            private Collection tolerableSpaces;
            public StaleTolerantQueryCache(
                        CacheProvider provider,
                        Properties props,
                        UpdateTimestampsCache updateTimestampsCache,
                        String regionName) throws HibernateException {
                super(provider, props, updateTimestampsCache, regionName);
                tolerableSpaces = parseTolerableSpaces(props);
            }
            protected boolean isUpToDate(Set spaces, Long timestamp) throws HibernateException {
                if ( tolerableSpaces.containsAll(spaces) ) {
                    return true;
                }
                else {
                    return super.isUpToDate(spaces, timestamp);
                }
            }
            private Collection parseTolerableSpaces(Properties props) {
                String spaces = props.getProperty("hibernate.cache.stale_tolerable_query_spaces");
                return Arrays.asList( StringHelper.split(", ", spaces) );
            }
        }
    

     

    Custom query cache implementations can be defined by implementing the net.sf.hibernate.cache.QueryCacheFactory interface and using that factory implementation class name as the value for the hibernate.cache.query_cache_factory configuration property (Environment.QUERY_CACHE_FACTORY).

    An example QueryCacheFactory which generates the above QueryCache impl might be:

        public class StaleTolerantQueryCacheFactory implements QueryCacheFactory {
            public QueryCache getQueryCache(
                    String regionName, 
                    CacheProvider provider, 
                    UpdateTimestampsCache updateTimestampsCache, 
                    Properties props) throws HibernateException {
                        return new StaleTolerantQueryCache(
                                provider, 
                                props, 
                                updateTimestampsCache, 
                                regionName
                        );
            }
        }