    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.dbcp2;
    018
    019import java.sql.Connection;
    020import java.sql.SQLException;
    021import java.sql.Statement;
    022import java.util.Arrays;
    023import java.util.Objects;
    024
    025import org.apache.commons.dbcp2.PoolingConnection.StatementType;
    026
    027/**
    028 * A key uniquely identifying {@link java.sql.PreparedStatement PreparedStatement}s.
    029 *
    030 * @since 2.0
    031 */
    032public class PStmtKey {
    033
    034    /**
    035     * Builder for prepareCall(String sql).
    036     */
    037    private class PreparedCallSQL implements StatementBuilder {
    038        @Override
    039        public Statement createStatement(final Connection connection) throws SQLException {
    040            return connection.prepareCall(sql);
    041        }
    042    }
    043
    044    /**
    045     * Builder for prepareCall(String sql, int resultSetType, int resultSetConcurrency).
    046     */
    047    private class PreparedCallWithResultSetConcurrency implements StatementBuilder {
    048        @Override
    049        public Statement createStatement(final Connection connection) throws SQLException {
    050            return connection.prepareCall(sql, resultSetType.intValue(), resultSetConcurrency.intValue());
    051        }
    052    }
    053
    054    /**
    055     * Builder for prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability).
    056     */
    057    private class PreparedCallWithResultSetHoldability implements StatementBuilder {
    058        @Override
    059        public Statement createStatement(final Connection connection) throws SQLException {
    060            return connection.prepareCall(sql, resultSetType.intValue(), resultSetConcurrency.intValue(),
    061                    resultSetHoldability.intValue());
    062        }
    063    }
    064
    065    /**
    066     * Builder for prepareStatement(String sql).
    067     */
    068    private class PreparedStatementSQL implements StatementBuilder {
    069        @Override
    070        public Statement createStatement(final Connection connection) throws SQLException {
    071            return connection.prepareStatement(sql);
    072        }
    073    }
    074
    075    /**
    076     * Builder for prepareStatement(String sql, int autoGeneratedKeys).
    077     */
    078    private class PreparedStatementWithAutoGeneratedKeys implements StatementBuilder {
    079        @Override
    080        public Statement createStatement(final Connection connection) throws SQLException {
    081            return connection.prepareStatement(sql, autoGeneratedKeys.intValue());
    082        }
    083    }
    084
    085    /**
    086     * Builder for prepareStatement(String sql, int[] columnIndexes).
    087     */
    088    private class PreparedStatementWithColumnIndexes implements StatementBuilder {
    089        @Override
    090        public Statement createStatement(final Connection connection) throws SQLException {
    091            return connection.prepareStatement(sql, columnIndexes);
    092        }
    093    }
    094
    095    /**
    096     * Builder for prepareStatement(String sql, String[] columnNames).
    097     */
    098    private class PreparedStatementWithColumnNames implements StatementBuilder {
    099        @Override
    100        public Statement createStatement(final Connection connection) throws SQLException {
    101            return connection.prepareStatement(sql, columnNames);
    102        }
    103    }
    104
    105    /**
    106     * Builder for prepareStatement(String sql, int resultSetType, int resultSetConcurrency).
    107     */
    108    private class PreparedStatementWithResultSetConcurrency implements StatementBuilder {
    109        @Override
    110        public Statement createStatement(final Connection connection) throws SQLException {
    111            return connection.prepareStatement(sql, resultSetType.intValue(), resultSetConcurrency.intValue());
    112        }
    113    }
    114
    115    /**
    116     * Builder for prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability).
    117     */
    118    private class PreparedStatementWithResultSetHoldability implements StatementBuilder {
    119        @Override
    120        public Statement createStatement(final Connection connection) throws SQLException {
    121            return connection.prepareStatement(sql, resultSetType.intValue(), resultSetConcurrency.intValue(),
    122                    resultSetHoldability.intValue());
    123        }
    124    }
    125
    126    /**
    127     * Interface for Prepared or Callable Statement.
    128     */
    129    private interface StatementBuilder {
    130        Statement createStatement(Connection connection) throws SQLException;
    131    }
    132
    133    /**
    134     * SQL defining Prepared or Callable Statement
    135     */
    136    private final String sql;
    137
    138    /**
    139     * Result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>, <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>,
    140     * or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    141     */
    142    private final Integer resultSetType;
    143
    144    /**
    145     * Result set concurrency. A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    146     * <code>ResultSet.CONCUR_UPDATABLE</code>.
    147     */
    148    private final Integer resultSetConcurrency;
    149
    150    /**
    151     * Result set holdability. One of the following <code>ResultSet</code> constants:
    152     * <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
    153     */
    154    private final Integer resultSetHoldability;
    155
    156    /** Database catalog. */
    157    private final String catalog;
    158
    159    /** Database schema. */
    160    private final String schema;
    161
    162    /**
    163     * A flag indicating whether auto-generated keys should be returned; one of
    164     * <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
    165     */
    166    private final Integer autoGeneratedKeys;
    167
    168    /**
    169     * An array of column indexes indicating the columns that should be returned from the inserted row or rows.
    170     */
    171    private final int[] columnIndexes;
    172
    173    /**
    174     * An array of column names indicating the columns that should be returned from the inserted row or rows.
    175     */
    176    private final String[] columnNames;
    177
    178    /**
    179     * Statement type, prepared or callable.
    180     */
    181    private final StatementType statementType;
    182
    183    /** Statement builder */
    184    private transient StatementBuilder builder;
    185
    186    /**
    187     * Constructs a key to uniquely identify a prepared statement.
    188     *
    189     * @param sql
    190     *            The SQL statement.
    191     * @deprecated Use {@link #PStmtKey(String, String, String)}.
    192     */
    193    @Deprecated
    194    public PStmtKey(final String sql) {
    195        this(sql, null, StatementType.PREPARED_STATEMENT);
    196    }
    197
    198    /**
    199     * Constructs a key to uniquely identify a prepared statement.
    200     *
    201     * @param sql
    202     *            The SQL statement.
    203     * @param resultSetType
    204     *            A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    205     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    206     * @param resultSetConcurrency
    207     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    208     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
    209     * @deprecated Use {@link #PStmtKey(String, String, String, int, int)}.
    210     */
    211    @Deprecated
    212    public PStmtKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
    213        this(sql, null, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
    214    }
    215
    216    /**
    217     * Constructs a key to uniquely identify a prepared statement.
    218     *
    219     * @param sql
    220     *            The SQL statement.
    221     * @param catalog
    222     *            The catalog.
    223     * @deprecated Use {@link #PStmtKey(String, String, String)}.
    224     */
    225    @Deprecated
    226    public PStmtKey(final String sql, final String catalog) {
    227        this(sql, catalog, StatementType.PREPARED_STATEMENT);
    228    }
    229
    230    /**
    231     * Constructs a key to uniquely identify a prepared statement.
    232     *
    233     * @param sql
    234     *            The SQL statement.
    235     * @param catalog
    236     *            The catalog.
    237     * @param autoGeneratedKeys
    238     *            A flag indicating whether auto-generated keys should be returned; one of
    239     *            <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
    240     * @deprecated Use {@link #PStmtKey(String, String, String, int)}.
    241     */
    242    @Deprecated
    243    public PStmtKey(final String sql, final String catalog, final int autoGeneratedKeys) {
    244        this(sql, catalog, StatementType.PREPARED_STATEMENT, Integer.valueOf(autoGeneratedKeys));
    245    }
    246
    247    /**
    248     * Constructs a key to uniquely identify a prepared statement.
    249     *
    250     * @param sql
    251     *            The SQL statement.
    252     * @param catalog
    253     *            The catalog.
    254     * @param resultSetType
    255     *            A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    256     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    257     * @param resultSetConcurrency
    258     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    259     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
    260     * @deprecated Use @link {@link #PStmtKey(String, String, String, int, int)}.
    261     */
    262    @Deprecated
    263    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency) {
    264        this(sql, catalog, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
    265    }
    266
    267    /**
    268     * Constructs a key to uniquely identify a prepared statement.
    269     *
    270     * @param sql
    271     *            The SQL statement.
    272     * @param catalog
    273     *            The catalog.
    274     * @param resultSetType
    275     *            a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    276     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    277     * @param resultSetConcurrency
    278     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    279     *            <code>ResultSet.CONCUR_UPDATABLE</code>
    280     * @param resultSetHoldability
    281     *            One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
    282     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
    283     * @deprecated Use {@link #PStmtKey(String, String, String, int, int, int)}.
    284     */
    285    @Deprecated
    286    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
    287            final int resultSetHoldability) {
    288        this(sql, catalog, resultSetType, resultSetConcurrency, resultSetHoldability, StatementType.PREPARED_STATEMENT);
    289    }
    290
    291    /**
    292     * Constructs a key to uniquely identify a prepared statement.
    293     *
    294     * @param sql
    295     *            The SQL statement.
    296     * @param catalog
    297     *            The catalog.
    298     * @param resultSetType
    299     *            a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    300     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
    301     * @param resultSetConcurrency
    302     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    303     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
    304     * @param resultSetHoldability
    305     *            One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
    306     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
    307     * @param statementType
    308     *            The SQL statement type, prepared or callable.
    309     * @deprecated Use {@link #PStmtKey(String, String, String, int, int, int, PoolingConnection.StatementType)}
    310     */
    311    @Deprecated
    312    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
    313            final int resultSetHoldability, final StatementType statementType) {
    314        this.sql = sql;
    315        this.catalog = catalog;
    316        this.schema = null;
    317        this.resultSetType = Integer.valueOf(resultSetType);
    318        this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
    319        this.resultSetHoldability = Integer.valueOf(resultSetHoldability);
    320        this.statementType = statementType;
    321        this.autoGeneratedKeys = null;
    322        this.columnIndexes = null;
    323        this.columnNames = null;
    324        // create builder
    325        if (statementType == StatementType.PREPARED_STATEMENT) {
    326            this.builder = new PreparedStatementWithResultSetHoldability();
    327        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
    328            this.builder = new PreparedCallWithResultSetHoldability();
    329        }
    330    }
    331
    332    /**
    333     * Constructs a key to uniquely identify a prepared statement.
    334     *
    335     * @param sql
    336     *            The SQL statement.
    337     * @param catalog
    338     *            The catalog.
    339     * @param resultSetType
    340     *            A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    341     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    342     * @param resultSetConcurrency
    343     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    344     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
    345     * @param statementType
    346     *            The SQL statement type, prepared or callable.
    347     * @deprecated Use {@link #PStmtKey(String, String, String, int, int, PoolingConnection.StatementType)}.
    348     */
    349    @Deprecated
    350    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
    351            final StatementType statementType) {
    352        this.sql = sql;
    353        this.catalog = catalog;
    354        this.schema = null;
    355        this.resultSetType = Integer.valueOf(resultSetType);
    356        this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
    357        this.resultSetHoldability = null;
    358        this.statementType = statementType;
    359        this.autoGeneratedKeys = null;
    360        this.columnIndexes = null;
    361        this.columnNames = null;
    362        // create builder
    363        if (statementType == StatementType.PREPARED_STATEMENT) {
    364            this.builder = new PreparedStatementWithResultSetConcurrency();
    365        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
    366            this.builder = new PreparedCallWithResultSetConcurrency();
    367        }
    368    }
    369
    370    /**
    371     * Constructs a key to uniquely identify a prepared statement.
    372     *
    373     * @param sql
    374     *            The SQL statement.
    375     * @param catalog
    376     *            The catalog.
    377     * @param columnIndexes
    378     *            An array of column indexes indicating the columns that should be returned from the inserted row or
    379     *            rows.
    380     * @deprecated Use {@link #PStmtKey(String, String, String, int[])}.
    381     */
    382    @Deprecated
    383    public PStmtKey(final String sql, final String catalog, final int[] columnIndexes) {
    384        this.sql = sql;
    385        this.catalog = catalog;
    386        this.schema = null;
    387        this.statementType = StatementType.PREPARED_STATEMENT;
    388        this.autoGeneratedKeys = null;
    389        this.columnIndexes = columnIndexes == null ? null : Arrays.copyOf(columnIndexes, columnIndexes.length);
    390        this.columnNames = null;
    391        this.resultSetType = null;
    392        this.resultSetConcurrency = null;
    393        this.resultSetHoldability = null;
    394        // create builder
    395        this.builder = new PreparedStatementWithColumnIndexes();
    396    }
    397
    398    /**
    399     * Constructs a key to uniquely identify a prepared statement.
    400     *
    401     * @param sql
    402     *            The SQL statement.
    403     * @param catalog
    404     *            The catalog.
    405     * @param statementType
    406     *            The SQL statement type, prepared or callable.
    407     * @deprecated Use {@link #PStmtKey(String, String, String, PoolingConnection.StatementType)}.
    408     */
    409    @Deprecated
    410    public PStmtKey(final String sql, final String catalog, final StatementType statementType) {
    411        this.sql = sql;
    412        this.catalog = catalog;
    413        this.schema = null;
    414        this.statementType = statementType;
    415        this.autoGeneratedKeys = null;
    416        this.columnIndexes = null;
    417        this.columnNames = null;
    418        this.resultSetType = null;
    419        this.resultSetConcurrency = null;
    420        this.resultSetHoldability = null;
    421        // create builder
    422        if (statementType == StatementType.PREPARED_STATEMENT) {
    423            this.builder = new PreparedStatementSQL();
    424        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
    425            this.builder = new PreparedCallSQL();
    426        }
    427    }
    428
    429    /**
    430     * Constructs a key to uniquely identify a prepared statement.
    431     *
    432     * @param sql
    433     *            The SQL statement.
    434     * @param catalog
    435     *            The catalog.
    436     * @param statementType
    437     *            The SQL statement type, prepared or callable.
    438     * @param autoGeneratedKeys
    439     *            A flag indicating whether auto-generated keys should be returned; one of
    440     *            <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
    441     * @deprecated Use {@link #PStmtKey(String, String, String, PoolingConnection.StatementType, Integer)}
    442     */
    443    @Deprecated
    444    public PStmtKey(final String sql, final String catalog, final StatementType statementType,
    445            final Integer autoGeneratedKeys) {
    446        this.sql = sql;
    447        this.catalog = catalog;
    448        this.schema = null;
    449        this.statementType = statementType;
    450        this.autoGeneratedKeys = autoGeneratedKeys;
    451        this.columnIndexes = null;
    452        this.columnNames = null;
    453        this.resultSetType = null;
    454        this.resultSetConcurrency = null;
    455        this.resultSetHoldability = null;
    456        // create builder
    457        if (statementType == StatementType.PREPARED_STATEMENT) {
    458            this.builder = new PreparedStatementWithAutoGeneratedKeys();
    459        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
    460            this.builder = new PreparedCallSQL();
    461        }
    462    }
    463
    464    /**
    465     * Constructs a key to uniquely identify a prepared statement.
    466     *
    467     * @param sql
    468     *            The SQL statement.
    469     * @param catalog
    470     *            The catalog.
    471     * @param schema
    472     *            The schema
    473     * @since 2.5.0
    474     */
    475    public PStmtKey(final String sql, final String catalog, final String schema) {
    476        this(sql, catalog, schema, StatementType.PREPARED_STATEMENT);
    477    }
    478
    479    /**
    480     * Constructs a key to uniquely identify a prepared statement.
    481     *
    482     * @param sql
    483     *            The SQL statement.
    484     * @param catalog
    485     *            The catalog.
    486     * @param schema
    487     *            The schema
    488     * @param autoGeneratedKeys
    489     *            A flag indicating whether auto-generated keys should be returned; one of
    490     *            <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
    491     * @since 2.5.0
    492     */
    493    public PStmtKey(final String sql, final String catalog, final String schema, final int autoGeneratedKeys) {
    494        this(sql, catalog, schema, StatementType.PREPARED_STATEMENT, Integer.valueOf(autoGeneratedKeys));
    495    }
    496
    497    /**
    498     * Constructs a key to uniquely identify a prepared statement.
    499     *
    500     * @param sql
    501     *            The SQL statement.
    502     * @param catalog
    503     *            The catalog.
    504     * @param schema
    505     *            The schema
    506     * @param resultSetType
    507     *            A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    508     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    509     * @param resultSetConcurrency
    510     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    511     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
    512     */
    513    public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency) {
    514        this(sql, catalog, schema, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
    515    }
    516
    517    /**
    518     * Constructs a key to uniquely identify a prepared statement.
    519     *
    520     * @param sql
    521     *            The SQL statement.
    522     * @param catalog
    523     *            The catalog.
    524     * @param schema
    525     *            The schema
    526     * @param resultSetType
    527     *            a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    528     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    529     * @param resultSetConcurrency
    530     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    531     *            <code>ResultSet.CONCUR_UPDATABLE</code>
    532     * @param resultSetHoldability
    533     *            One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
    534     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
    535     * @since 2.5.0
    536     */
    537    public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
    538            final int resultSetHoldability) {
    539        this(sql, catalog, schema, resultSetType, resultSetConcurrency, resultSetHoldability, StatementType.PREPARED_STATEMENT);
    540    }
    541
    542    /**
    543     * Constructs a key to uniquely identify a prepared statement.
    544     *
    545     * @param sql
    546     *            The SQL statement.
    547     * @param catalog
    548     *            The catalog.
    549     * @param schema
    550     *            The schema.
    551     * @param resultSetType
    552     *            a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    553     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
    554     * @param resultSetConcurrency
    555     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    556     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
    557     * @param resultSetHoldability
    558     *            One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
    559     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
    560     * @param statementType
    561     *            The SQL statement type, prepared or callable.
    562     * @since 2.5.0
    563     */
    564    public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
    565            final int resultSetHoldability, final StatementType statementType) {
    566        this.sql = sql;
    567        this.catalog = catalog;
    568        this.schema = schema;
    569        this.resultSetType = Integer.valueOf(resultSetType);
    570        this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
    571        this.resultSetHoldability = Integer.valueOf(resultSetHoldability);
    572        this.statementType = statementType;
    573        this.autoGeneratedKeys = null;
    574        this.columnIndexes = null;
    575        this.columnNames = null;
    576        // create builder
    577        if (statementType == StatementType.PREPARED_STATEMENT) {
    578            this.builder = new PreparedStatementWithResultSetHoldability();
    579        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
    580            this.builder = new PreparedCallWithResultSetHoldability();
    581        }
    582    }
    583
    584    /**
    585     * Constructs a key to uniquely identify a prepared statement.
    586     *
    587     * @param sql
    588     *            The SQL statement.
    589     * @param catalog
    590     *            The catalog.
    591     * @param schema
    592     *            The schema.
    593     * @param resultSetType
    594     *            A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    595     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    596     * @param resultSetConcurrency
    597     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    598     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
    599     * @param statementType
    600     *            The SQL statement type, prepared or callable.
    601     * @since 2.5.0
    602     */
    603    public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency,
    604            final StatementType statementType) {
    605        this.sql = sql;
    606        this.catalog = catalog;
    607        this.schema = schema;
    608        this.resultSetType = Integer.valueOf(resultSetType);
    609        this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
    610        this.resultSetHoldability = null;
    611        this.statementType = statementType;
    612        this.autoGeneratedKeys = null;
    613        this.columnIndexes = null;
    614        this.columnNames = null;
    615        // create builder
    616        if (statementType == StatementType.PREPARED_STATEMENT) {
    617            this.builder = new PreparedStatementWithResultSetConcurrency();
    618        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
    619            this.builder = new PreparedCallWithResultSetConcurrency();
    620        }
    621    }
    622
    623    /**
    624     * Constructs a key to uniquely identify a prepared statement.
    625     *
    626     * @param sql
    627     *            The SQL statement.
    628     * @param catalog
    629     *            The catalog.
    630     * @param schema
    631     *            The schema.
    632     * @param columnIndexes
    633     *            An array of column indexes indicating the columns that should be returned from the inserted row or
    634     *            rows.
    635     */
    636    public PStmtKey(final String sql, final String catalog, final String schema, final int[] columnIndexes) {
    637        this.sql = sql;
    638        this.catalog = catalog;
    639        this.schema = schema;
    640        this.statementType = StatementType.PREPARED_STATEMENT;
    641        this.autoGeneratedKeys = null;
    642        this.columnIndexes = columnIndexes == null ? null : Arrays.copyOf(columnIndexes, columnIndexes.length);
    643        this.columnNames = null;
    644        this.resultSetType = null;
    645        this.resultSetConcurrency = null;
    646        this.resultSetHoldability = null;
    647        // create builder
    648        this.builder = new PreparedStatementWithColumnIndexes();
    649    }
    650
    651    /**
    652     * Constructs a key to uniquely identify a prepared statement.
    653     *
    654     * @param sql
    655     *            The SQL statement.
    656     * @param catalog
    657     *            The catalog.
    658     * @param schema
    659     *            The schema.
    660     * @param statementType
    661     *            The SQL statement type, prepared or callable.
    662     * @since 2.5.0
    663     */
    664    public PStmtKey(final String sql, final String catalog, final String schema, final StatementType statementType) {
    665        this.sql = sql;
    666        this.catalog = catalog;
    667        this.schema = schema;
    668        this.statementType = statementType;
    669        this.autoGeneratedKeys = null;
    670        this.columnIndexes = null;
    671        this.columnNames = null;
    672        this.resultSetType = null;
    673        this.resultSetConcurrency = null;
    674        this.resultSetHoldability = null;
    675        // create builder
    676        if (statementType == StatementType.PREPARED_STATEMENT) {
    677            this.builder = new PreparedStatementSQL();
    678        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
    679            this.builder = new PreparedCallSQL();
    680        }
    681    }
    682
    683    /**
    684     * Constructs a key to uniquely identify a prepared statement.
    685     *
    686     * @param sql
    687     *            The SQL statement.
    688     * @param catalog
    689     *            The catalog.
    690     * @param schema
    691     *            The schema.
    692     * @param statementType
    693     *            The SQL statement type, prepared or callable.
    694     * @param autoGeneratedKeys
    695     *            A flag indicating whether auto-generated keys should be returned; one of
    696     *            <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
    697     * @since 2.5.0
    698     */
    699    public PStmtKey(final String sql, final String catalog, final String schema, final StatementType statementType,
    700            final Integer autoGeneratedKeys) {
    701        this.sql = sql;
    702        this.catalog = catalog;
    703        this.schema = schema;
    704        this.statementType = statementType;
    705        this.autoGeneratedKeys = autoGeneratedKeys;
    706        this.columnIndexes = null;
    707        this.columnNames = null;
    708        this.resultSetType = null;
    709        this.resultSetConcurrency = null;
    710        this.resultSetHoldability = null;
    711        // create builder
    712        if (statementType == StatementType.PREPARED_STATEMENT) {
    713            this.builder = new PreparedStatementWithAutoGeneratedKeys();
    714        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
    715            this.builder = new PreparedCallSQL();
    716        }
    717    }
    718
    719    /**
    720     * Constructs a key to uniquely identify a prepared statement.
    721     *
    722     * @param sql
    723     *            The SQL statement.
    724     * @param catalog
    725     *            The catalog.
    726     * @param schema
    727     *            The schema.
    728     * @param columnNames
    729     *            An array of column names indicating the columns that should be returned from the inserted row or rows.
    730     * @since 2.5.0
    731     */
    732    public PStmtKey(final String sql, final String catalog, final String schema, final String[] columnNames) {
    733        this.sql = sql;
    734        this.catalog = catalog;
    735        this.schema = schema;
    736        this.statementType = StatementType.PREPARED_STATEMENT;
    737        this.autoGeneratedKeys = null;
    738        this.columnIndexes = null;
    739        this.columnNames = columnNames == null ? null : Arrays.copyOf(columnNames, columnNames.length);
    740        this.resultSetType = null;
    741        this.resultSetConcurrency = null;
    742        this.resultSetHoldability = null;
    743        // create builder
    744        builder = new PreparedStatementWithColumnNames();
    745    }
    746
    747    /**
    748     * Constructs a key to uniquely identify a prepared statement.
    749     *
    750     * @param sql
    751     *            The SQL statement.
    752     * @param catalog
    753     *            The catalog.
    754     * @param columnNames
    755     *            An array of column names indicating the columns that should be returned from the inserted row or rows.
    756     * @deprecated Use {@link #PStmtKey(String, String, String, String[])}.
    757     */
    758    @Deprecated
    759    public PStmtKey(final String sql, final String catalog, final String[] columnNames) {
    760        this.sql = sql;
    761        this.catalog = catalog;
    762        this.schema = null;
    763        this.statementType = StatementType.PREPARED_STATEMENT;
    764        this.autoGeneratedKeys = null;
    765        this.columnIndexes = null;
    766        this.columnNames = columnNames == null ? null : Arrays.copyOf(columnNames, columnNames.length);
    767        this.resultSetType = null;
    768        this.resultSetConcurrency = null;
    769        this.resultSetHoldability = null;
    770        // create builder
    771        builder = new PreparedStatementWithColumnNames();
    772    }
    773
    774    /**
    775     * Creates a new Statement from the given Connection.
    776     *
    777     * @param connection
    778     *            The Connection to use to create the statement.
    779     * @return The statement.
    780     * @throws SQLException
    781     *             Thrown when there is a problem creating the statement.
    782     */
    783    public Statement createStatement(final Connection connection) throws SQLException {
    784        if (builder == null) {
    785            throw new IllegalStateException("Prepared statement key is invalid.");
    786        }
    787        return builder.createStatement(connection);
    788    }
    789
    790    @Override
    791    public boolean equals(final Object obj) {
    792        if (this == obj) {
    793            return true;
    794        }
    795        if (obj == null) {
    796            return false;
    797        }
    798        if (getClass() != obj.getClass()) {
    799            return false;
    800        }
    801        final PStmtKey other = (PStmtKey) obj;
    802        if (!Objects.equals(autoGeneratedKeys, other.autoGeneratedKeys)) {
    803            return false;
    804        }
    805        if (!Objects.equals(catalog, other.catalog)) {
    806            return false;
    807        }
    808        if (!Arrays.equals(columnIndexes, other.columnIndexes)) {
    809            return false;
    810        }
    811        if (!Arrays.equals(columnNames, other.columnNames)) {
    812            return false;
    813        }
    814        if (!Objects.equals(resultSetConcurrency, other.resultSetConcurrency)) {
    815            return false;
    816        }
    817        if (!Objects.equals(resultSetHoldability, other.resultSetHoldability)) {
    818            return false;
    819        }
    820        if (!Objects.equals(resultSetType, other.resultSetType)) {
    821            return false;
    822        }
    823        if (!Objects.equals(schema, other.schema)) {
    824            return false;
    825        }
    826        if (!Objects.equals(sql, other.sql)) {
    827            return false;
    828        }
    829        if (statementType != other.statementType) {
    830            return false;
    831        }
    832        return true;
    833    }
    834
    835    /**
    836     * Gets a flag indicating whether auto-generated keys should be returned; one of
    837     * <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
    838     *
    839     * @return a flag indicating whether auto-generated keys should be returned.
    840     */
    841    public Integer getAutoGeneratedKeys() {
    842        return autoGeneratedKeys;
    843    }
    844
    845    /**
    846     * The catalog.
    847     *
    848     * @return The catalog.
    849     */
    850    public String getCatalog() {
    851        return catalog;
    852    }
    853
    854    /**
    855     * Gets an array of column indexes indicating the columns that should be returned from the inserted row or rows.
    856     *
    857     * @return An array of column indexes.
    858     */
    859    public int[] getColumnIndexes() {
    860        return columnIndexes;
    861    }
    862
    863    /**
    864     * Gets an array of column names indicating the columns that should be returned from the inserted row or rows.
    865     *
    866     * @return An array of column names.
    867     */
    868    public String[] getColumnNames() {
    869        return columnNames;
    870    }
    871
    872    /**
    873     * Gets the result set concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    874     * <code>ResultSet.CONCUR_UPDATABLE</code>.
    875     *
    876     * @return The result set concurrency type.
    877     */
    878    public Integer getResultSetConcurrency() {
    879        return resultSetConcurrency;
    880    }
    881
    882    /**
    883     * Gets the result set holdability, one of the following <code>ResultSet</code> constants:
    884     * <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
    885     *
    886     * @return The result set holdability.
    887     */
    888    public Integer getResultSetHoldability() {
    889        return resultSetHoldability;
    890    }
    891
    892    /**
    893     * Gets the result set type, one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    894     * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    895     *
    896     * @return the result set type.
    897     */
    898    public Integer getResultSetType() {
    899        return resultSetType;
    900    }
    901
    902    /**
    903     * The schema.
    904     *
    905     * @return The catalog.
    906     */
    907    public String getSchema() {
    908        return schema;
    909    }
    910
    911    /**
    912     * Gets the SQL statement.
    913     *
    914     * @return the SQL statement.
    915     */
    916    public String getSql() {
    917        return sql;
    918    }
    919
    920    /**
    921     * The SQL statement type.
    922     *
    923     * @return The SQL statement type.
    924     */
    925    public StatementType getStmtType() {
    926        return statementType;
    927    }
    928
    929    @Override
    930    public int hashCode() {
    931        final int prime = 31;
    932        int result = 1;
    933        result = prime * result + ((autoGeneratedKeys == null) ? 0 : autoGeneratedKeys.hashCode());
    934        result = prime * result + ((catalog == null) ? 0 : catalog.hashCode());
    935        result = prime * result + Arrays.hashCode(columnIndexes);
    936        result = prime * result + Arrays.hashCode(columnNames);
    937        result = prime * result + ((resultSetConcurrency == null) ? 0 : resultSetConcurrency.hashCode());
    938        result = prime * result + ((resultSetHoldability == null) ? 0 : resultSetHoldability.hashCode());
    939        result = prime * result + ((resultSetType == null) ? 0 : resultSetType.hashCode());
    940        result = prime * result + ((schema == null) ? 0 : schema.hashCode());
    941        result = prime * result + ((sql == null) ? 0 : sql.hashCode());
    942        result = prime * result + ((statementType == null) ? 0 : statementType.hashCode());
    943        return result;
    944    }
    945
    946    @Override
    947    public String toString() {
    948        final StringBuffer buf = new StringBuffer();
    949        buf.append("PStmtKey: sql=");
    950        buf.append(sql);
    951        buf.append(", catalog=");
    952        buf.append(catalog);
    953        buf.append(", schema=");
    954        buf.append(schema);
    955        buf.append(", resultSetType=");
    956        buf.append(resultSetType);
    957        buf.append(", resultSetConcurrency=");
    958        buf.append(resultSetConcurrency);
    959        buf.append(", resultSetHoldability=");
    960        buf.append(resultSetHoldability);
    961        buf.append(", autoGeneratedKeys=");
    962        buf.append(autoGeneratedKeys);
    963        buf.append(", columnIndexes=");
    964        buf.append(Arrays.toString(columnIndexes));
    965        buf.append(", columnNames=");
    966        buf.append(Arrays.toString(columnNames));
    967        buf.append(", statementType=");
    968        buf.append(statementType);
    969        return buf.toString();
    970    }
    971}