PoolingConnection.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.dbcp2;

  18. import java.sql.CallableStatement;
  19. import java.sql.Connection;
  20. import java.sql.PreparedStatement;
  21. import java.sql.SQLException;
  22. import java.sql.Statement;
  23. import java.util.NoSuchElementException;
  24. import java.util.Objects;

  25. import org.apache.commons.pool2.KeyedObjectPool;
  26. import org.apache.commons.pool2.KeyedPooledObjectFactory;
  27. import org.apache.commons.pool2.PooledObject;
  28. import org.apache.commons.pool2.impl.DefaultPooledObject;
  29. import org.apache.commons.pool2.impl.GenericKeyedObjectPool;

  30. /**
  31.  * A {@link DelegatingConnection} that pools {@link PreparedStatement}s.
  32.  * <p>
  33.  * The {@link #prepareStatement} and {@link #prepareCall} methods, rather than creating a new PreparedStatement each
  34.  * time, may actually pull the statement from a pool of unused statements. The {@link PreparedStatement#close} method of
  35.  * the returned statement doesn't actually close the statement, but rather returns it to the pool. (See
  36.  * {@link PoolablePreparedStatement}, {@link PoolableCallableStatement}.)
  37.  * </p>
  38.  *
  39.  * @see PoolablePreparedStatement
  40.  * @since 2.0
  41.  */
  42. public class PoolingConnection extends DelegatingConnection<Connection>
  43.         implements KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> {

  44.     /**
  45.      * Statement types.
  46.      *
  47.      * See subclasses of {@link Statement}.
  48.      *
  49.      * @since 2.0 protected enum.
  50.      * @since 2.4.0 public enum.
  51.      * @see Statement
  52.      * @see CallableStatement
  53.      * @see PreparedStatement
  54.      */
  55.     public enum StatementType {

  56.         /**
  57.          * Callable statement.
  58.          *
  59.          * @see CallableStatement
  60.          */
  61.         CALLABLE_STATEMENT,

  62.         /**
  63.          * Prepared statement.
  64.          *
  65.          * @see PreparedStatement
  66.          */
  67.         PREPARED_STATEMENT
  68.     }

  69.     /** Pool of {@link PreparedStatement}s. and {@link CallableStatement}s */
  70.     private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pStmtPool;

  71.     private boolean clearStatementPoolOnReturn;

  72.     /**
  73.      * Constructs a new instance.
  74.      *
  75.      * @param connection
  76.      *            the underlying {@link Connection}.
  77.      */
  78.     public PoolingConnection(final Connection connection) {
  79.         super(connection);
  80.     }

  81.     /**
  82.      * {@link KeyedPooledObjectFactory} method for activating pooled statements.
  83.      *
  84.      * @param key
  85.      *            ignored
  86.      * @param pooledObject
  87.      *            wrapped pooled statement to be activated
  88.      */
  89.     @Override
  90.     public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
  91.             throws SQLException {
  92.         pooledObject.getObject().activate();
  93.     }

  94.     /**
  95.      * Closes and frees all {@link PreparedStatement}s or {@link CallableStatement}s from the pool, and close the
  96.      * underlying connection.
  97.      */
  98.     @Override
  99.     public synchronized void close() throws SQLException {
  100.         try {
  101.             if (null != pStmtPool) {
  102.                 final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> oldPool = pStmtPool;
  103.                 pStmtPool = null;
  104.                 try {
  105.                     oldPool.close();
  106.                 } catch (final RuntimeException e) {
  107.                     throw e;
  108.                 } catch (final Exception e) {
  109.                     throw new SQLException("Cannot close connection", e);
  110.                 }
  111.             }
  112.         } finally {
  113.             try {
  114.                 @SuppressWarnings("resource")
  115.                 final Connection delegateInternal = getDelegateInternal();
  116.                 if (delegateInternal != null) {
  117.                     delegateInternal.close();
  118.                 }
  119.             } finally {
  120.                 setClosedInternal(true);
  121.             }
  122.         }
  123.     }

  124.     /**
  125.      * Notification from {@link PoolableConnection} that we returned to the pool.
  126.      *
  127.      * @throws SQLException when {@code clearStatementPoolOnReturn} is true and the statement pool could not be
  128.      *                      cleared
  129.      * @since 2.8.0
  130.      */
  131.     public void connectionReturnedToPool() throws SQLException {
  132.         if (pStmtPool != null && clearStatementPoolOnReturn) {
  133.             try {
  134.                 pStmtPool.clear();
  135.             } catch (final Exception e) {
  136.                 throw new SQLException("Error clearing statement pool", e);
  137.             }
  138.         }
  139.     }

  140.     /**
  141.      * Creates a PStmtKey for the given arguments.
  142.      *
  143.      * @param sql
  144.      *            the SQL string used to define the statement
  145.      *
  146.      * @return the PStmtKey created for the given arguments.
  147.      */
  148.     protected PStmtKey createKey(final String sql) {
  149.         return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull());
  150.     }

  151.     /**
  152.      * Creates a PStmtKey for the given arguments.
  153.      *
  154.      * @param sql
  155.      *            the SQL string used to define the statement
  156.      * @param autoGeneratedKeys
  157.      *            A flag indicating whether auto-generated keys should be returned; one of
  158.      *            {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}.
  159.      *
  160.      * @return the PStmtKey created for the given arguments.
  161.      */
  162.     protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) {
  163.         return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), autoGeneratedKeys);
  164.     }

  165.     /**
  166.      * Creates a PStmtKey for the given arguments.
  167.      *
  168.      * @param sql
  169.      *            the SQL string used to define the statement
  170.      * @param resultSetType
  171.      *            result set type
  172.      * @param resultSetConcurrency
  173.      *            result set concurrency
  174.      *
  175.      * @return the PStmtKey created for the given arguments.
  176.      */
  177.     protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
  178.         return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency);
  179.     }

  180.     /**
  181.      * Creates a PStmtKey for the given arguments.
  182.      *
  183.      * @param sql
  184.      *            the SQL string used to define the statement
  185.      * @param resultSetType
  186.      *            result set type
  187.      * @param resultSetConcurrency
  188.      *            result set concurrency
  189.      * @param resultSetHoldability
  190.      *            result set holdability
  191.      *
  192.      * @return the PStmtKey created for the given arguments.
  193.      */
  194.     protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
  195.             final int resultSetHoldability) {
  196.         return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency,
  197.                 resultSetHoldability);
  198.     }

  199.     /**
  200.      * Creates a PStmtKey for the given arguments.
  201.      *
  202.      * @param sql
  203.      *            the SQL string used to define the statement
  204.      * @param resultSetType
  205.      *            result set type
  206.      * @param resultSetConcurrency
  207.      *            result set concurrency
  208.      * @param resultSetHoldability
  209.      *            result set holdability
  210.      * @param statementType
  211.      *            statement type
  212.      *
  213.      * @return the PStmtKey created for the given arguments.
  214.      */
  215.     protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
  216.             final int resultSetHoldability, final StatementType statementType) {
  217.         return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency,
  218.                 resultSetHoldability, statementType);
  219.     }

  220.     /**
  221.      * Creates a PStmtKey for the given arguments.
  222.      *
  223.      * @param sql
  224.      *            the SQL string used to define the statement
  225.      * @param resultSetType
  226.      *            result set type
  227.      * @param resultSetConcurrency
  228.      *            result set concurrency
  229.      * @param statementType
  230.      *            statement type
  231.      *
  232.      * @return the PStmtKey created for the given arguments.
  233.      */
  234.     protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency,
  235.             final StatementType statementType) {
  236.         return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, statementType);
  237.     }

  238.     /**
  239.      * Creates a PStmtKey for the given arguments.
  240.      *
  241.      * @param sql
  242.      *            the SQL string used to define the statement
  243.      * @param columnIndexes
  244.      *            An array of column indexes indicating the columns that should be returned from the inserted row or
  245.      *            rows.
  246.      *
  247.      * @return the PStmtKey created for the given arguments.
  248.      */
  249.     protected PStmtKey createKey(final String sql, final int[] columnIndexes) {
  250.         return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnIndexes);
  251.     }

  252.     /**
  253.      * Creates a PStmtKey for the given arguments.
  254.      *
  255.      * @param sql
  256.      *            the SQL string used to define the statement
  257.      * @param statementType
  258.      *            statement type
  259.      *
  260.      * @return the PStmtKey created for the given arguments.
  261.      */
  262.     protected PStmtKey createKey(final String sql, final StatementType statementType) {
  263.         return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), statementType, null);
  264.     }

  265.     /**
  266.      * Creates a PStmtKey for the given arguments.
  267.      *
  268.      * @param sql
  269.      *            the SQL string used to define the statement
  270.      * @param columnNames
  271.      *            column names
  272.      *
  273.      * @return the PStmtKey created for the given arguments.
  274.      */
  275.     protected PStmtKey createKey(final String sql, final String[] columnNames) {
  276.         return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnNames);
  277.     }

  278.     /**
  279.      * {@link KeyedPooledObjectFactory} method for destroying PoolablePreparedStatements and PoolableCallableStatements.
  280.      * Closes the underlying statement.
  281.      *
  282.      * @param key
  283.      *            ignored
  284.      * @param pooledObject
  285.      *            the wrapped pooled statement to be destroyed.
  286.      */
  287.     @Override
  288.     public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) throws SQLException {
  289.         if (pooledObject != null) {
  290.             @SuppressWarnings("resource")
  291.             final DelegatingPreparedStatement object = pooledObject.getObject();
  292.             if (object != null) {
  293.                 @SuppressWarnings("resource")
  294.                 final Statement innermostDelegate = object.getInnermostDelegate();
  295.                 if (innermostDelegate != null) {
  296.                     innermostDelegate.close();
  297.                 }
  298.             }
  299.         }
  300.     }

  301.     private String getCatalogOrNull() {
  302.         try {
  303.             return getCatalog();
  304.         } catch (final SQLException ignored) {
  305.             return null;
  306.         }
  307.     }

  308.     private String getSchemaOrNull() {
  309.         try {
  310.             return getSchema();
  311.         } catch (final SQLException ignored) {
  312.             return null;
  313.         }
  314.     }

  315.     /**
  316.      * Gets the prepared statement pool.
  317.      *
  318.      * @return statement pool
  319.      * @since 2.8.0
  320.      */
  321.     public KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> getStatementPool() {
  322.         return pStmtPool;
  323.     }

  324.     /**
  325.      * {@link KeyedPooledObjectFactory} method for creating {@link PoolablePreparedStatement}s or
  326.      * {@link PoolableCallableStatement}s. The {@code stmtType} field in the key determines whether a
  327.      * PoolablePreparedStatement or PoolableCallableStatement is created.
  328.      *
  329.      * @param key
  330.      *            the key for the {@link PreparedStatement} to be created
  331.      * @see #createKey(String, int, int, StatementType)
  332.      */
  333.     @SuppressWarnings("resource")
  334.     @Override
  335.     public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws SQLException {
  336.         if (null == key) {
  337.             throw new IllegalArgumentException("Prepared statement key is null or invalid.");
  338.         }
  339.         if (key.getStmtType() == StatementType.PREPARED_STATEMENT) {
  340.             final PreparedStatement statement = (PreparedStatement) key.createStatement(getDelegate());
  341.             @SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this
  342.             final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pStmtPool, this);
  343.             return new DefaultPooledObject<>(pps);
  344.         }
  345.         final CallableStatement statement = (CallableStatement) key.createStatement(getDelegate());
  346.         final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pStmtPool, this);
  347.         return new DefaultPooledObject<>(pcs);
  348.     }

  349.     /**
  350.      * Normalizes the given SQL statement, producing a canonical form that is semantically equivalent to the original.
  351.      *
  352.      * @param sql The statement to be normalized.
  353.      *
  354.      * @return The canonical form of the supplied SQL statement.
  355.      */
  356.     protected String normalizeSQL(final String sql) {
  357.         return sql.trim();
  358.     }

  359.     /**
  360.      * {@link KeyedPooledObjectFactory} method for passivating {@link PreparedStatement}s or {@link CallableStatement}s.
  361.      * Invokes {@link PreparedStatement#clearParameters}.
  362.      *
  363.      * @param key
  364.      *            ignored
  365.      * @param pooledObject
  366.      *            a wrapped {@link PreparedStatement}
  367.      */
  368.     @Override
  369.     public void passivateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
  370.             throws SQLException {
  371.         @SuppressWarnings("resource")
  372.         final DelegatingPreparedStatement dps = pooledObject.getObject();
  373.         dps.clearParameters();
  374.         dps.passivate();
  375.     }

  376.     /**
  377.      * Creates or obtains a {@link CallableStatement} from the pool.
  378.      *
  379.      * @param key
  380.      *            a {@link PStmtKey} for the given arguments
  381.      * @return a {@link PoolableCallableStatement}
  382.      * @throws SQLException
  383.      *             Wraps an underlying exception.
  384.      */
  385.     private CallableStatement prepareCall(final PStmtKey key) throws SQLException {
  386.         return (CallableStatement) prepareStatement(key);
  387.     }

  388.     /**
  389.      * Creates or obtains a {@link CallableStatement} from the pool.
  390.      *
  391.      * @param sql
  392.      *            the SQL string used to define the CallableStatement
  393.      * @return a {@link PoolableCallableStatement}
  394.      * @throws SQLException
  395.      *             Wraps an underlying exception.
  396.      */
  397.     @Override
  398.     public CallableStatement prepareCall(final String sql) throws SQLException {
  399.         return prepareCall(createKey(sql, StatementType.CALLABLE_STATEMENT));
  400.     }

  401.     /**
  402.      * Creates or obtains a {@link CallableStatement} from the pool.
  403.      *
  404.      * @param sql
  405.      *            the SQL string used to define the CallableStatement
  406.      * @param resultSetType
  407.      *            result set type
  408.      * @param resultSetConcurrency
  409.      *            result set concurrency
  410.      * @return a {@link PoolableCallableStatement}
  411.      * @throws SQLException
  412.      *             Wraps an underlying exception.
  413.      */
  414.     @Override
  415.     public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
  416.             throws SQLException {
  417.         return prepareCall(createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT));
  418.     }

  419.     /**
  420.      * Creates or obtains a {@link CallableStatement} from the pool.
  421.      *
  422.      * @param sql
  423.      *            the SQL string used to define the CallableStatement
  424.      * @param resultSetType
  425.      *            result set type
  426.      * @param resultSetConcurrency
  427.      *            result set concurrency
  428.      * @param resultSetHoldability
  429.      *            result set holdability
  430.      * @return a {@link PoolableCallableStatement}
  431.      * @throws SQLException
  432.      *             Wraps an underlying exception.
  433.      */
  434.     @Override
  435.     public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
  436.             final int resultSetHoldability) throws SQLException {
  437.         return prepareCall(createKey(sql, resultSetType, resultSetConcurrency,
  438.                 resultSetHoldability, StatementType.CALLABLE_STATEMENT));
  439.     }

  440.     /**
  441.      * Creates or obtains a {@link PreparedStatement} from the pool.
  442.      *
  443.      * @param key
  444.      *            a {@link PStmtKey} for the given arguments
  445.      * @return a {@link PoolablePreparedStatement}
  446.      * @throws SQLException
  447.      *             Wraps an underlying exception.
  448.      */
  449.     private PreparedStatement prepareStatement(final PStmtKey key) throws SQLException {
  450.         if (null == pStmtPool) {
  451.             throw new SQLException("Statement pool is null - closed or invalid PoolingConnection.");
  452.         }
  453.         try {
  454.             return pStmtPool.borrowObject(key);
  455.         } catch (final NoSuchElementException e) {
  456.             throw new SQLException("MaxOpenPreparedStatements limit reached", e);
  457.         } catch (final RuntimeException e) {
  458.             throw e;
  459.         } catch (final Exception e) {
  460.             throw new SQLException("Borrow prepareStatement from pool failed", e);
  461.         }
  462.     }

  463.     /**
  464.      * Creates or obtains a {@link PreparedStatement} from the pool.
  465.      *
  466.      * @param sql
  467.      *            the SQL string used to define the PreparedStatement
  468.      * @return a {@link PoolablePreparedStatement}
  469.      * @throws SQLException
  470.      *             Wraps an underlying exception.
  471.      */
  472.     @Override
  473.     public PreparedStatement prepareStatement(final String sql) throws SQLException {
  474.         return prepareStatement(createKey(sql));
  475.     }

  476.     /*
  477.      * Creates or obtains a {@link PreparedStatement} from the pool.
  478.      *
  479.      * @param sql
  480.      *            the SQL string used to define the PreparedStatement
  481.      * @param autoGeneratedKeys
  482.      *            A flag indicating whether auto-generated keys should be returned; one of
  483.      *            {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}.
  484.      * @return a {@link PoolablePreparedStatement}
  485.      * @throws SQLException
  486.      *             Wraps an underlying exception.
  487.      */
  488.     @Override
  489.     public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
  490.         return prepareStatement(createKey(sql, autoGeneratedKeys));
  491.     }

  492.     /**
  493.      * Creates or obtains a {@link PreparedStatement} from the pool.
  494.      *
  495.      * @param sql
  496.      *            the SQL string used to define the PreparedStatement
  497.      * @param resultSetType
  498.      *            result set type
  499.      * @param resultSetConcurrency
  500.      *            result set concurrency
  501.      * @return a {@link PoolablePreparedStatement}
  502.      * @throws SQLException
  503.      *             Wraps an underlying exception.
  504.      */
  505.     @Override
  506.     public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
  507.             throws SQLException {
  508.         return prepareStatement(createKey(sql, resultSetType, resultSetConcurrency));
  509.     }

  510.     /**
  511.      * Creates or obtains a {@link PreparedStatement} from the pool.
  512.      *
  513.      * @param sql
  514.      *            the SQL string used to define the PreparedStatement
  515.      * @param resultSetType
  516.      *            result set type
  517.      * @param resultSetConcurrency
  518.      *            result set concurrency
  519.      * @param resultSetHoldability
  520.      *            result set holdability
  521.      * @return a {@link PoolablePreparedStatement}
  522.      * @throws SQLException
  523.      *             Wraps an underlying exception.
  524.      */
  525.     @Override
  526.     public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
  527.             final int resultSetHoldability) throws SQLException {
  528.         return prepareStatement(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
  529.     }

  530.     /**
  531.      * Creates or obtains a {@link PreparedStatement} from the pool.
  532.      *
  533.      * @param sql
  534.      *            the SQL string used to define the PreparedStatement
  535.      * @param columnIndexes
  536.      *            An array of column indexes indicating the columns that should be returned from the inserted row or
  537.      *            rows.
  538.      * @return a {@link PoolablePreparedStatement}
  539.      * @throws SQLException
  540.      *             Wraps an underlying exception.
  541.      */
  542.     @Override
  543.     public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
  544.         return prepareStatement(createKey(sql, columnIndexes));
  545.     }

  546.     /**
  547.      * Creates or obtains a {@link PreparedStatement} from the pool.
  548.      *
  549.      * @param sql
  550.      *            the SQL string used to define the PreparedStatement
  551.      * @param columnNames
  552.      *            column names
  553.      * @return a {@link PoolablePreparedStatement}
  554.      * @throws SQLException
  555.      *             Wraps an underlying exception.
  556.      */
  557.     @Override
  558.     public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
  559.         return prepareStatement(createKey(sql, columnNames));
  560.     }

  561.     /**
  562.      * Sets whether the pool of statements should be cleared when the connection is returned to its pool.
  563.      * Default is false.
  564.      *
  565.      * @param clearStatementPoolOnReturn clear or not
  566.      * @since 2.8.0
  567.      */
  568.     public void setClearStatementPoolOnReturn(final boolean clearStatementPoolOnReturn) {
  569.         this.clearStatementPoolOnReturn = clearStatementPoolOnReturn;
  570.     }

  571.     /**
  572.      * Sets the prepared statement pool.
  573.      *
  574.      * @param pool
  575.      *            the prepared statement pool.
  576.      */
  577.     public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pool) {
  578.         pStmtPool = pool;
  579.     }

  580.     @Override
  581.     public synchronized String toString() {
  582.         if (pStmtPool instanceof GenericKeyedObjectPool) {
  583.             // DBCP-596 PoolingConnection.toString() causes StackOverflowError
  584.             final GenericKeyedObjectPool<?, ?> gkop = (GenericKeyedObjectPool<?, ?>) pStmtPool;
  585.             if (gkop.getFactory() == this) {
  586.                 return "PoolingConnection: " + pStmtPool.getClass() + "@" + System.identityHashCode(pStmtPool);
  587.             }
  588.         }
  589.         return "PoolingConnection: " + Objects.toString(pStmtPool);
  590.     }

  591.     /**
  592.      * {@link KeyedPooledObjectFactory} method for validating pooled statements. Currently, always returns true.
  593.      *
  594.      * @param key
  595.      *            ignored
  596.      * @param pooledObject
  597.      *            ignored
  598.      * @return {@code true}
  599.      */
  600.     @Override
  601.     public boolean validateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) {
  602.         return true;
  603.     }
  604. }