Package com.jolbox.bonecp

Source Code of com.jolbox.bonecp.StatementCache

/**
*  Copyright 2010 Wallace Wadge
*
*    Licensed under the Apache License, Version 2.0 (the "License");
*    you may not use this file except in compliance with the License.
*    You may obtain a copy of the License at
*
*        http://www.apache.org/licenses/LICENSE-2.0
*
*    Unless required by applicable law or agreed to in writing, software
*    distributed under the License is distributed on an "AS IS" BASIS,
*    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*    See the License for the specific language governing permissions and
*    limitations under the License.
*/
package com.jolbox.bonecp;

import java.sql.SQLException;
import java.util.concurrent.ConcurrentMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.MapMaker;


/**
* JDBC statement cache.
*
* @author wallacew
*/
public class StatementCache implements IStatementCache {
  /** Logger class. */
  private static Logger logger = LoggerFactory.getLogger(StatementCache.class);
  /** The cache of our statements. */
  private ConcurrentMap<String, StatementHandle> cache;
  /** How many items to cache. */
  private int cacheSize;
  /** If true, keep statistics. */
  private final boolean maintainStats;
  /** Statistics handle. */
  private final Statistics statistics;
 
  /**
   * Creates a statement cache of given size.
   *
   * @param size of cache.
   * @param maintainStats if true, keep track of statistics.
   * @param statistics statistics handle.
   */
  public StatementCache(int size, boolean maintainStats, Statistics statistics){
    this.maintainStats = maintainStats;
    this.statistics = statistics;
    this.cache = new MapMaker()
    .concurrencyLevel(32)
    .makeMap();

    this.cacheSize = size;
  }

  /** Simply appends the given parameters and returns it to obtain a cache key
   * @param sql
   * @param resultSetConcurrency
   * @param resultSetHoldability
   * @param resultSetType
   * @return cache key to use
   */
  public String calculateCacheKey(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability){
    StringBuilder tmp = calculateCacheKeyInternal(sql, resultSetType,
        resultSetConcurrency);

    tmp.append(", H:");
    tmp.append(resultSetHoldability);

    return tmp.toString();
  }

  /** Cache key calculation.
   * @param sql string
   * @param resultSetType
   * @param resultSetConcurrency
   * @return cache key
   */
  public String calculateCacheKey(String sql, int resultSetType, int resultSetConcurrency){
    StringBuilder tmp = calculateCacheKeyInternal(sql, resultSetType,
        resultSetConcurrency);

    return tmp.toString();
  }

  /** Cache key calculation.
   * @param sql
   * @param resultSetType
   * @param resultSetConcurrency
   * @return cache key
   */
  private StringBuilder calculateCacheKeyInternal(String sql,
      int resultSetType, int resultSetConcurrency) {
    StringBuilder tmp = new StringBuilder(sql.length()+20);
    tmp.append(sql);

    tmp.append(", T");
    tmp.append(resultSetType);
    tmp.append(", C");
    tmp.append(resultSetConcurrency);
    return tmp;
  }


  /** Alternate version of autoGeneratedKeys.
   * @param sql
   * @param autoGeneratedKeys
   * @return cache key to use.
   */
  public String calculateCacheKey(String sql, int autoGeneratedKeys) {
    StringBuilder tmp = new StringBuilder(sql.length()+4);
    tmp.append(sql);
    tmp.append(autoGeneratedKeys);
    return tmp.toString();
  }

  /** Calculate a cache key.
   * @param sql to use
   * @param columnIndexes to use
   * @return cache key to use.
   */
  public String calculateCacheKey(String sql, int[] columnIndexes) {
    StringBuilder tmp = new StringBuilder(sql.length()+4);
    tmp.append(sql);
    for (int i=0; i < columnIndexes.length; i++){
      tmp.append(columnIndexes[i]);
      tmp.append("CI,");
    }
    return tmp.toString();
  }

  /** Calculate a cache key.
   * @param sql to use
   * @param columnNames to use
   * @return cache key to use.
   */
  public String calculateCacheKey(String sql, String[] columnNames) {
    StringBuilder tmp = new StringBuilder(sql.length()+4);
    tmp.append(sql);
    for (int i=0; i < columnNames.length; i++){
      tmp.append(columnNames[i]);
      tmp.append("CN,");
    }
    return tmp.toString();

  }

  /**
   * {@inheritDoc}
   *
   * @see com.jolbox.bonecp.IStatementCache#get(java.lang.String)
   */
//  @Override
  public StatementHandle get(String key){
    StatementHandle statement = this.cache.get(key);
   
    if (statement != null && (statement.isEnqueuedForClosure() || !statement.logicallyClosed.compareAndSet(true, false))){
      statement = null;
    }
   
    if (this.maintainStats){
      if (statement != null){
        this.statistics.incrementCacheHits();
      } else {
        this.statistics.incrementCacheMiss();
      }
    }
    return statement;
  }

  // @Override
  public StatementHandle get(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) {
    return get(calculateCacheKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
  }


  // @Override
  public StatementHandle get(String sql, int resultSetType, int resultSetConcurrency) {
    return get(calculateCacheKey(sql, resultSetType, resultSetConcurrency));
  }

  // @Override
  public StatementHandle get(String sql, int autoGeneratedKeys) {
    return get(calculateCacheKey(sql, autoGeneratedKeys));
  }


  // @Override
  public StatementHandle get(String sql, int[] columnIndexes) {
    return get(calculateCacheKey(sql, columnIndexes));
  }


  // @Override
  public StatementHandle get(String sql, String[] columnNames) {
    return get(calculateCacheKey(sql, columnNames));
  }



  /**
   * {@inheritDoc}
   *
   * @see com.jolbox.bonecp.IStatementCache#size()
   */
  // @Override
  public int size(){
    return this.cache.size();
  }



  /**
   * {@inheritDoc}
   *
   * @see com.jolbox.bonecp.IStatementCache#clear()
   */
  // @Override
  public void clear() {
    for (StatementHandle statement: this.cache.values()){
      try {
        if (!(statement.isClosed() || statement.isEnqueuedForClosure())){ // this might race with statement release helper but nothing bad should occur
          statement.close();
        }
      } catch (SQLException e) {
        // don't log, we might fail if the connection link has died
        // logger.error("Error closing off statement", e);
      }
    }
    this.cache.clear();
  }

  // @Override
  public void checkForProperClosure() {
    for (StatementHandle statement: this.cache.values()){
      if (!statement.isClosedOrEnqueuedForClosure()){
        logger.error(statement.isClosed()+" " + statement.isEnqueuedForClosure()+"Statement not closed properly in application\n\n"+statement.getOpenStackTrace());
      }
    }   
  }

  @Override
  public void putIfAbsent(String key, StatementHandle handle) {
    if (this.cache.size() <=  this.cacheSize && key != null){ // perhaps use LRU in future?? Worth the overhead? Hmm....
      if (this.cache.putIfAbsent(key, handle) == null){
        handle.inCache = true;
        if (this.maintainStats){
          this.statistics.incrementStatementsCached();
        }
      }
    }

  }

}
TOP

Related Classes of com.jolbox.bonecp.StatementCache

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.