Open Source Repository

Home /hibernate/hibernate-3.2.7.ga | Repository Home


org/hibernate/cache/StandardQueryCache.java
//$Id: StandardQueryCache.java 10118 2006-07-13 21:38:41Z [email protected] $
package org.hibernate.cache;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.hibernate.HibernateException;
import org.hibernate.UnresolvableObjectException;
import org.hibernate.cfg.Settings;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.type.Type;
import org.hibernate.type.TypeFactory;

/**
 * The standard implementation of the Hibernate QueryCache interface.  This
 * implementation is very good at recognizing stale query results and
 * and re-running queries when it detects this condition, recaching the new
 * results.
 *
 @author Gavin King
 */
public class StandardQueryCache implements QueryCache {

  private static final Log log = LogFactory.getLogStandardQueryCache.class );

  private Cache queryCache;
  private UpdateTimestampsCache updateTimestampsCache;
  private final String regionName;

  public void clear() throws CacheException {
    queryCache.clear();
  }

  public StandardQueryCache(
      final Settings settings, 
      final Properties props, 
      final UpdateTimestampsCache updateTimestampsCache, 
      String regionNamethrows HibernateException {
    if regionName == null ) {
      regionName = StandardQueryCache.class.getName();
    }
    String prefix = settings.getCacheRegionPrefix();
    if prefix != null ) {
      regionName = prefix + '.' + regionName;
    }
    log.info"starting query cache at region: " + regionName );

    this.queryCache = settings.getCacheProvider().buildCache(regionName, props);
    this.updateTimestampsCache = updateTimestampsCache;
    this.regionName = regionName;
  }

  public boolean put(
      QueryKey key,
      Type[] returnTypes,
      List result,
      boolean isNaturalKeyLookup,
      SessionImplementor sessionthrows HibernateException {
    
    if isNaturalKeyLookup && result.size()==) {
      return false;
    }
    else {
      Long ts = new Longsession.getTimestamp() );

      if log.isDebugEnabled() ) {
        log.debug"caching query results in region: " + regionName + "; timestamp=" + ts );
      }
      
      List cacheable = new ArrayListresult.size()+);
      cacheable.addts );
      for int i=0; i<result.size(); i++ ) {
        if returnTypes.length==) {
          cacheable.addreturnTypes[0].disassembleresult.get(i), session, null ) );
        }
        else {
          cacheable.addTypeFactory.disassemble( (Object[]) result.get(i), returnTypes, null, session, null ) );
        }
      }
      
      queryCache.put(key, cacheable);
      
      return true;
      
    }

  }

  public List get(
      QueryKey key,
      Type[] returnTypes,
      boolean isNaturalKeyLookup,
      Set spaces,
      SessionImplementor sessionthrows HibernateException {
    if log.isDebugEnabled() ) {
      log.debug("checking cached query results in region: " + regionName);
    }

    List cacheable = (ListqueryCache.get(key);
    if (cacheable==null) {
      log.debug("query results were not found in cache");
      return null;
    }

    Long timestamp = (Longcacheable.get(0);
    if !isNaturalKeyLookup && !isUpToDate(spaces, timestamp) ) {
      log.debug("cached query results were not up to date");
      return null;
    }

    log.debug("returning cached query results");
    for int i=1; i<cacheable.size(); i++ ) {
      if returnTypes.length==) {
        returnTypes[0].beforeAssemble( (Serializablecacheable.get(i), session );
      }
      else {
        TypeFactory.beforeAssemble( (Serializable[]) cacheable.get(i), returnTypes, session );
      }
    }
    List result = new ArrayListcacheable.size()-);
    for int i=1; i<cacheable.size(); i++ ) {
      try {
        if returnTypes.length==) {
          result.addreturnTypes[0].assemble( (Serializablecacheable.get(i), session, null ) );
        }
        else {
          result.addTypeFactory.assemble( (Serializable[]) cacheable.get(i), returnTypes, session, null ) );
        }
      }
      catch (UnresolvableObjectException uoe) {
        if (isNaturalKeyLookup) {
          //TODO: not really completely correct, since
          //      the uoe could occur while resolving
          //      associations, leaving the PC in an
          //      inconsistent state
          log.debug("could not reassemble cached result set");
          queryCache.remove(key);
          return null;
        }
        else {
          throw uoe;
        }
      }
    }
    return result;
  }

  protected boolean isUpToDate(Set spaces, Long timestamp) {
    if log.isDebugEnabled() ) {
      log.debug("Checking query spaces for up-to-dateness: " + spaces);
    }
    return updateTimestampsCache.isUpToDate(spaces, timestamp);
  }

  public void destroy() {
    try {
      queryCache.destroy();
    }
    catch (Exception e) {
      log.warn("could not destroy query cache: " + regionName, e);
    }
  }
  
  public Cache getCache() {
    return queryCache;
  }
  
  public String getRegionName() {
    return regionName;
  }
  
  public String toString() {
    return "StandardQueryCache(" + regionName + ')';
  }

}