All Downloads are FREE. Search and download functionalities are using the official Maven repository.

snaq.db.CachedStatement Maven / Gradle / Ivy

Go to download

JDBC connection pooling utility, supporting time-based expiry, statement caching, connection validation, and easy configuration using a pool manager.

There is a newer version: 7.0.1-jdk7
Show newest version
/*
  ---------------------------------------------------------------------------
  DBPool : Java Database Connection Pooling 
  Copyright (c) 2001-2013 Giles Winstanley. All Rights Reserved.

  This is file is part of the DBPool project, which is licensed under
  the BSD-style licence terms shown below.
  ---------------------------------------------------------------------------
  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

  3. The name of the author may not be used to endorse or promote products
  derived from this software without specific prior written permission.

  4. Redistributions of modified versions of the source code, must be
  accompanied by documentation detailing which parts of the code are not part
  of the original software.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  ---------------------------------------------------------------------------
 */
package snaq.db;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;

/**
 * {@link Statement} wrapper that provides methods for caching support.
 *
 * @author Giles Winstanley
 */
public class CachedStatement implements Statement
{
  /** Exception message for trying to used a closed statement. */
  protected static final String MSG_STATEMENT_CLOSED = "Statement is closed";
  /** Assigned {@code StatementListener} instance. */
  private StatementListener listener;
  /** Delegate {@code Statement} instance. */
  protected Statement st;
  /** Flag indicating whether the connection is open. */
  private boolean open = true;
  /** Flag indicating a temporary "checking" status (used internally). */
  protected boolean checking = false;
  /** Flag indicating whether the statement can be cached. */
  protected boolean cacheable = false;

  /**
   * Creates a new {@link CachedStatement} instance, using the supplied {@link Statement}.
   * @param st {@code Statement} instance to which database calls should be delegated
   */
  public CachedStatement(Statement st)
  {
    this.st = st;
  }

  /**
   * Added to provide support for checking cached statement type.
   */
  void setChecking(boolean b)
  {
    checking = b;
  }

  /**
   * Sets whether this statement can be cached/recycled.
   */
  void setCacheable(boolean b)
  {
    cacheable = b;
  }

  /**
   * Returns whether this statement can be cached/recycled.
   */
  boolean isCacheable()
  {
    return cacheable;
  }

  /**
   * Returns a string description of the {@link ResultSet} parameters.
   * @return A string description of the {@link ResultSet} parameters
   */
  protected String getParametersString()
  {
    StringBuilder sb = new StringBuilder();
    try
    {
      switch(getResultSetType())
      {
        case ResultSet.TYPE_SCROLL_INSENSITIVE:
          sb.append("TYPE_SCROLL_INSENSITIVE");
          break;
        case ResultSet.TYPE_SCROLL_SENSITIVE:
          sb.append("TYPE_SCROLL_SENSITIVE");
          break;
        default:
          sb.append("TYPE_FORWARD_ONLY");
      }
    }
    catch (SQLException sqlx)
    {
      sb.append("TYPE_UNKNOWN");
    }
    sb.append(',');
    try
    {
      switch(getResultSetConcurrency())
      {
        case ResultSet.CONCUR_UPDATABLE:
          sb.append("CONCUR_UPDATABLE");
          break;
        default:
          sb.append("CONCUR_READ_ONLY");
      }
    }
    catch (SQLException sqlx)
    {
      sb.append("CONCUR_UNKNOWN");
    }
    sb.append(',');
    try
    {
      switch(getResultSetHoldability())
      {
        case ResultSet.CLOSE_CURSORS_AT_COMMIT:
          sb.append("CLOSE_CURSORS_AT_COMMIT");
          break;
        case ResultSet.HOLD_CURSORS_OVER_COMMIT:
          sb.append("HOLD_CURSORS_OVER_COMMIT");
      }
    }
    catch (SQLException sqlx)
    {
      sb.append("HOLD_UNKNOWN");
    }
    return sb.toString();
  }

  // Cleans up the statement ready to be reused or closed.
  public void recycle() throws SQLException
  {
    ResultSet rs = st.getResultSet();
    if (rs != null)
      rs.close();

    try
    {
      st.clearWarnings();
    }
    catch (SQLException sqlx)  // Caught to fix bug in some drivers.
    {
    }

    try
    {
      st.clearBatch();
    }
    catch (SQLException sqlx)  // Caught to fix bug in some drivers.
    {
    }
  }

  /**
   * Overridden to provide caching support.
   */
  @Override
  public void close() throws SQLException
  {
    if (!open)
      return;
    open = false;
    // If listener registered, do callback, otherwise release statement.
    if (listener != null)
      listener.statementClosed(this);
    else
      release();
  }

  /**
   * Overridden to provide caching support.
   * @throws SQLException if thrown while attempting to release statement
   */
  public void release() throws SQLException
  {
    st.close();
    st = null;  // Clear delegate to ensure no possible reuse.
    setStatementListener(null);
  }

  /**
   * Added to provide caching support.
   */
  void setOpen()
  {
    open = true;
  }

  /**
   * Added to provide caching support.
   */
  void setStatementListener(StatementListener x)
  {
    this.listener = x;
  }

  //**********************************
  // Interface methods from JDBC 2.0
  //**********************************

  @Override
  public ResultSet executeQuery(String sql) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.executeQuery(sql);
  }

  @Override
  public int executeUpdate(String sql) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.executeUpdate(sql);
  }

  @Override
  public int getMaxFieldSize() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getMaxFieldSize();
  }

  @Override
  public void setMaxFieldSize(int max) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    st.setMaxFieldSize(max);
  }

  @Override
  public int getMaxRows() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getMaxRows();
  }

  @Override
  public void setMaxRows(int max) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    st.setMaxRows(max);
  }

  @Override
  public void setEscapeProcessing(boolean enable) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    st.setEscapeProcessing(enable);
  }

  @Override
  public int getQueryTimeout() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getQueryTimeout();
  }

  @Override
  public void setQueryTimeout(int seconds) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    st.setQueryTimeout(seconds);
  }

  @Override
  public void cancel() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    st.cancel();
  }

  @Override
  public SQLWarning getWarnings() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getWarnings();
  }

  @Override
  public void clearWarnings() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    st.clearWarnings();
  }

  @Override
  public void setCursorName(String name) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    st.setCursorName(name);
  }

  @Override
  public boolean execute(String sql) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.execute(sql);
  }

  @Override
  public ResultSet getResultSet() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getResultSet();
  }

  @Override
  public int getUpdateCount() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getUpdateCount();
  }

  @Override
  public boolean getMoreResults() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getMoreResults();
  }

  @Override
  public void setFetchDirection(int direction) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    st.setFetchDirection(direction);
  }

  @Override
  public int getFetchDirection() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getFetchDirection();
  }

  @Override
  public void setFetchSize(int rows) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    st.setFetchSize(rows);
  }

  @Override
  public int getFetchSize() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getFetchSize();
  }

  @Override
  public int getResultSetConcurrency() throws SQLException
  {
    if (!open && !checking)
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getResultSetConcurrency();
  }

  @Override
  public int getResultSetType() throws SQLException
  {
    if (!open && !checking)
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getResultSetType();
  }

  @Override
  public void addBatch(String sql) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    st.addBatch(sql);
  }

  @Override
  public void clearBatch() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    st.clearBatch();
  }

  @Override
  public int[] executeBatch() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.executeBatch();
  }

  @Override
  public Connection getConnection() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getConnection();
  }

  //**********************************
  // Interface methods from JDBC 3.0
  //**********************************

  @Override
  public boolean getMoreResults(int current) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getMoreResults(current);
  }

  @Override
  public ResultSet getGeneratedKeys() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getGeneratedKeys();
  }

  @Override
  public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.executeUpdate(sql, autoGeneratedKeys);
  }

  @Override
  public int executeUpdate(String sql, int[] columnIndexes) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.executeUpdate(sql, columnIndexes);
  }

  @Override
  public int executeUpdate(String sql, String[] columnNames) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.executeUpdate(sql, columnNames);
  }

  @Override
  public boolean execute(String sql, int autoGeneratedKeys) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.execute(sql, autoGeneratedKeys);
  }

  @Override
  public boolean execute(String sql, int[] columnIndexes) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.execute(sql, columnIndexes);
  }

  @Override
  public boolean execute(String sql, String[] columnNames) throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.execute(sql, columnNames);
  }

  @Override
  public int getResultSetHoldability() throws SQLException
  {
    if (!open && !checking)
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.getResultSetHoldability();
  }

  //**********************************
  // Interface methods from JDBC 4.0
  //**********************************
  // --- JDBC 4.0 ---
  @Override
  public boolean isWrapperFor(Class iface) throws SQLException
  {
    return iface.isInstance(st);
  }

  @Override
  public  T unwrap(Class iface) throws SQLException
  {
    try
    {
      // Disallow recycling of this statement if unwrapped.
      T x = iface.cast(st);
      cacheable = false;
      return x;
    }
    catch (ClassCastException ccx)
    {
      throw new SQLException("Invalid type specified for unwrap operation: " + iface.getName(), ccx);
    }
  }

  @Override
  public boolean isClosed() throws SQLException
  {
    return !open;
  }

  /**
   * Sets whether this statement should be considered poolable.
   * If a statement is already flagged as unpoolable it cannot be made poolable.
   * @param poolable flag indicating desired poolability
   * @throws SQLException if the request cannot be fulfilled
   */
  @Override
  public void setPoolable(boolean poolable) throws SQLException
  {
    if (poolable && !cacheable)
      throw new SQLException("Cannot enable pooling on this statement");
    setCacheable(poolable);
  }

  /**
   * Returns whether this statement is poolable.
   * @throws SQLException if the request cannot be fulfilled
   */
  @Override
  public boolean isPoolable() throws SQLException
  {
    return isCacheable();
  }
  // --- End JDBC 4.0 ---

  //**********************************
  // Interface methods from JDBC 4.1
  //**********************************
  // --- JDBC 4.1 ---
  @Override
  public void closeOnCompletion() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    st.closeOnCompletion();
  }

  @Override
  public boolean isCloseOnCompletion() throws SQLException
  {
    if (isClosed())
      throw new SQLException(MSG_STATEMENT_CLOSED);
    return st.isCloseOnCompletion();
  }
  // --- End JDBC 4.1 ---
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy