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