    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.PreparedStatement;
    021import java.sql.SQLException;
    022import java.sql.Statement;
    023import java.util.Arrays;
    024
    025import org.apache.commons.dbcp2.PoolingConnection.StatementType;
    026
    027/**
    028 * A key uniquely identifying {@link java.sql.PreparedStatement PreparedStatement}s.
    029 * @since 2.0
    030 */
    031public class PStmtKey {
    032
    033    /** SQL defining Prepared or Callable Statement */
    034    private final String _sql;
    035
    036    /** Result set type */
    037    private final Integer _resultSetType;
    038
    039    /** Result set concurrency */
    040    private final Integer _resultSetConcurrency;
    041
    042    /** Result set holdability */
    043    private final Integer _resultSetHoldability;
    044
    045    /** Database catalog */
    046    private final String _catalog;
    047
    048    /** Auto generated keys */
    049    private final Integer _autoGeneratedKeys;
    050
    051    /** column indexes */
    052    private final int[] _columnIndexes;
    053
    054    /** column names */
    055    private final String[] _columnNames;
    056
    057    /** Statement type */
    058    private final StatementType _stmtType;
    059
    060    /** Statement builder */
    061    private StatementBuilder builder;
    062
    063    public PStmtKey(final String sql) {
    064        this(sql, null, StatementType.PREPARED_STATEMENT);
    065    }
    066
    067    public PStmtKey(final String sql, final String catalog) {
    068        this(sql, catalog, StatementType.PREPARED_STATEMENT);
    069    }
    070
    071    public PStmtKey(final String sql, final String catalog, final StatementType stmtType) {
    072        _sql = sql;
    073        _catalog = catalog;
    074        _stmtType = stmtType;
    075        _autoGeneratedKeys = null;
    076        _columnIndexes = null;
    077        _columnNames = null;
    078        _resultSetType = null;
    079        _resultSetConcurrency = null;
    080        _resultSetHoldability = null;
    081        // create builder
    082        if (stmtType == StatementType.PREPARED_STATEMENT) {
    083            builder = new PreparedStatementSQL();
    084        } else if (stmtType == StatementType.CALLABLE_STATEMENT) {
    085            builder = new PreparedCallSQL();
    086        }
    087    }
    088
    089    public PStmtKey(final String sql, final String catalog, final int autoGeneratedKeys) {
    090        this(sql, catalog, StatementType.PREPARED_STATEMENT, Integer.valueOf(autoGeneratedKeys));
    091    }
    092
    093    public PStmtKey(final String sql, final String catalog, final StatementType stmtType, final Integer autoGeneratedKeys) {
    094        _sql = sql;
    095        _catalog = catalog;
    096        _stmtType = stmtType;
    097        _autoGeneratedKeys = autoGeneratedKeys;
    098        _columnIndexes = null;
    099        _columnNames = null;
    100        _resultSetType = null;
    101        _resultSetConcurrency = null;
    102        _resultSetHoldability = null;
    103        // create builder
    104        if (stmtType == StatementType.PREPARED_STATEMENT) {
    105            builder = new PreparedStatementWithAutoGeneratedKeys();
    106        } else if (stmtType == StatementType.CALLABLE_STATEMENT) {
    107            builder = new PreparedCallSQL();
    108        }
    109    }
    110
    111    public PStmtKey(final String sql, final String catalog, final int[] columnIndexes) {
    112        _sql = sql;
    113        _catalog = catalog;
    114        _stmtType = StatementType.PREPARED_STATEMENT;
    115        _autoGeneratedKeys = null;
    116        _columnIndexes = columnIndexes;
    117        _columnNames = null;
    118        _resultSetType = null;
    119        _resultSetConcurrency = null;
    120        _resultSetHoldability = null;
    121        // create builder
    122        builder = new PreparedStatementWithColumnIndexes();
    123    }
    124
    125    public PStmtKey(final String sql, final String catalog, final String[] columnNames) {
    126        _sql = sql;
    127        _catalog = catalog;
    128        _stmtType = StatementType.PREPARED_STATEMENT;
    129        _autoGeneratedKeys = null;
    130        _columnIndexes = null;
    131        _columnNames = columnNames;
    132        _resultSetType = null;
    133        _resultSetConcurrency = null;
    134        _resultSetHoldability = null;
    135        // create builder
    136        builder = new PreparedStatementWithColumnNames();
    137    }
    138
    139    public  PStmtKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
    140        this(sql, null, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
    141    }
    142
    143    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency) {
    144        this(sql, catalog, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
    145    }
    146
    147    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency, final StatementType stmtType) {
    148        _sql = sql;
    149        _catalog = catalog;
    150        _resultSetType = Integer.valueOf(resultSetType);
    151        _resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
    152        _resultSetHoldability = null;
    153        _stmtType = stmtType;
    154        _autoGeneratedKeys = null;
    155        _columnIndexes = null;
    156        _columnNames = null;
    157        // create builder
    158        if (stmtType == StatementType.PREPARED_STATEMENT) {
    159            builder = new PreparedStatementWithResultSetConcurrency();
    160        } else if (stmtType == StatementType.CALLABLE_STATEMENT) {
    161            builder = new PreparedCallWithResultSetConcurrency();
    162        }
    163    }
    164
    165    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
    166            final int resultSetHoldability) {
    167        this(sql, catalog, resultSetType, resultSetConcurrency, resultSetHoldability, StatementType.PREPARED_STATEMENT);
    168    }
    169
    170    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
    171            final int resultSetHoldability, final StatementType stmtType) {
    172        _sql = sql;
    173        _catalog = catalog;
    174        _resultSetType = Integer.valueOf(resultSetType);
    175        _resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
    176        _resultSetHoldability = Integer.valueOf(resultSetHoldability);
    177        _stmtType = stmtType;
    178        _autoGeneratedKeys = null;
    179        _columnIndexes = null;
    180        _columnNames = null;
    181        // create builder
    182        if (stmtType == StatementType.PREPARED_STATEMENT) {
    183            builder = new PreparedStatementWithResultSetHoldability();
    184        } else if (stmtType == StatementType.CALLABLE_STATEMENT) {
    185            builder = new PreparedCallWithResultSetHoldability();
    186        }
    187    }
    188
    189
    190    public String getSql() {
    191        return _sql;
    192    }
    193
    194    public Integer getResultSetType() {
    195        return _resultSetType;
    196    }
    197
    198    public Integer getResultSetConcurrency() {
    199        return _resultSetConcurrency;
    200    }
    201
    202    public Integer getResultSetHoldability() {
    203        return _resultSetHoldability;
    204    }
    205
    206    public Integer getAutoGeneratedKeys() {
    207        return _autoGeneratedKeys;
    208    }
    209
    210    public int[] getColumnIndexes() {
    211        return _columnIndexes;
    212    }
    213
    214    public String[] getColumnNames() {
    215        return _columnNames;
    216    }
    217
    218    public String getCatalog() {
    219        return _catalog;
    220    }
    221
    222    public StatementType getStmtType() {
    223        return _stmtType;
    224    }
    225
    226    @Override
    227    public boolean equals(final Object obj) {
    228        if (this == obj) {
    229            return true;
    230        }
    231        if (obj == null) {
    232            return false;
    233        }
    234        if (getClass() != obj.getClass()) {
    235            return false;
    236        }
    237        final PStmtKey other = (PStmtKey) obj;
    238        if (_catalog == null) {
    239            if (other._catalog != null) {
    240                return false;
    241            }
    242        } else if (!_catalog.equals(other._catalog)) {
    243            return false;
    244        }
    245        if (_resultSetConcurrency == null) {
    246            if (other._resultSetConcurrency != null) {
    247                return false;
    248            }
    249        } else if (!_resultSetConcurrency.equals(other._resultSetConcurrency)) {
    250            return false;
    251        }
    252        if (_resultSetType == null) {
    253            if (other._resultSetType != null) {
    254                return false;
    255            }
    256        } else if (!_resultSetType.equals(other._resultSetType)) {
    257            return false;
    258        }
    259        if (_resultSetHoldability == null) {
    260            if (other._resultSetHoldability != null) {
    261                return false;
    262            }
    263        } else if (!_resultSetHoldability.equals(other._resultSetHoldability)) {
    264            return false;
    265        }
    266        if (_autoGeneratedKeys == null) {
    267            if (other._autoGeneratedKeys != null) {
    268                return false;
    269            }
    270        } else if (!_autoGeneratedKeys.equals(other._autoGeneratedKeys)) {
    271            return false;
    272        }
    273        if (!Arrays.equals(_columnIndexes, other._columnIndexes)) {
    274            return false;
    275        }
    276        if (!Arrays.equals(_columnNames, other._columnNames)) {
    277            return false;
    278        }
    279        if (_sql == null) {
    280            if (other._sql != null) {
    281                return false;
    282            }
    283        } else if (!_sql.equals(other._sql)) {
    284            return false;
    285        }
    286        if (_stmtType != other._stmtType) {
    287            return false;
    288        }
    289        return true;
    290    }
    291
    292    @Override
    293    public int hashCode() {
    294        final int prime = 31;
    295        int result = 1;
    296        result = prime * result + (_catalog == null ? 0 : _catalog.hashCode());
    297        result = prime * result + (_resultSetConcurrency == null ? 0 : _resultSetConcurrency.hashCode());
    298        result = prime * result + (_resultSetType == null ? 0 : _resultSetType.hashCode());
    299        result = prime * result + (_resultSetHoldability == null ? 0 : _resultSetHoldability.hashCode());
    300        result = prime * result + (_sql == null ? 0 : _sql.hashCode());
    301        result = prime * result + (_autoGeneratedKeys == null ? 0 : _autoGeneratedKeys.hashCode());
    302        result = prime * result + Arrays.hashCode(_columnIndexes);
    303        result = prime * result + Arrays.hashCode(_columnNames);
    304        result = prime * result + _stmtType.hashCode();
    305        return result;
    306    }
    307
    308    @Override
    309    public String toString() {
    310        final StringBuffer buf = new StringBuffer();
    311        buf.append("PStmtKey: sql=");
    312        buf.append(_sql);
    313        buf.append(", catalog=");
    314        buf.append(_catalog);
    315        buf.append(", resultSetType=");
    316        buf.append(_resultSetType);
    317        buf.append(", resultSetConcurrency=");
    318        buf.append(_resultSetConcurrency);
    319        buf.append(", resultSetHoldability=");
    320        buf.append(_resultSetHoldability);
    321        buf.append(", autoGeneratedKeys=");
    322        buf.append(_autoGeneratedKeys);
    323        buf.append(", columnIndexes=");
    324        buf.append(Arrays.toString(_columnIndexes));
    325        buf.append(", columnNames=");
    326        buf.append(Arrays.toString(_columnNames));
    327        buf.append(", statementType=");
    328        buf.append(_stmtType);
    329        return buf.toString();
    330    }
    331
    332    public Statement createStatement(final Connection connection) throws SQLException {
    333        if (builder == null) {
    334            throw new IllegalStateException("Prepared statement key is invalid.");
    335        }
    336        return builder.createStatement(connection);
    337    }
    338
    339    /**
    340     * Interface for Prepared or Callable Statement
    341     */
    342    private interface StatementBuilder {
    343        public Statement createStatement(Connection connection) throws SQLException;
    344    }
    345
    346    /**
    347     * Builder for prepareStatement(String sql)
    348     */
    349    private class PreparedStatementSQL implements StatementBuilder {
    350        @Override
    351        public Statement createStatement(final Connection connection) throws SQLException {
    352            final PreparedStatement statement = connection.prepareStatement(_sql);
    353            return statement;
    354        }
    355    }
    356
    357    /**
    358     * Builder for prepareStatement(String sql, int autoGeneratedKeys)
    359     */
    360    private class PreparedStatementWithAutoGeneratedKeys implements StatementBuilder {
    361        @Override
    362        public Statement createStatement(final Connection connection) throws SQLException {
    363            final PreparedStatement statement = connection.prepareStatement(
    364                    _sql, _autoGeneratedKeys.intValue());
    365            return statement;
    366        }
    367    }
    368
    369    /**
    370     * Builder for prepareStatement(String sql, int[] columnIndexes)
    371     */
    372    private class PreparedStatementWithColumnIndexes implements StatementBuilder {
    373        @Override
    374        public Statement createStatement(final Connection connection) throws SQLException {
    375            final PreparedStatement statement = connection.prepareStatement(
    376                    _sql, _columnIndexes);
    377            return statement;
    378        }
    379    }
    380
    381    /**
    382     * Builder for prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
    383     */
    384    private class PreparedStatementWithResultSetConcurrency implements StatementBuilder {
    385        @Override
    386        public Statement createStatement(final Connection connection) throws SQLException {
    387            final PreparedStatement statement = connection.prepareStatement(
    388                    _sql, _resultSetType.intValue(), _resultSetConcurrency.intValue());
    389            return statement;
    390        }
    391    }
    392
    393    /**
    394     * Builder for prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
    395     */
    396    private class PreparedStatementWithResultSetHoldability implements StatementBuilder {
    397        @Override
    398        public Statement createStatement(final Connection connection) throws SQLException {
    399            final PreparedStatement statement = connection.prepareStatement(
    400                    _sql, _resultSetType.intValue(), _resultSetConcurrency.intValue(),
    401                    _resultSetHoldability.intValue());
    402            return statement;
    403        }
    404    }
    405
    406    /**
    407     * Builder for prepareStatement(String sql, String[] columnNames)
    408     */
    409    private class PreparedStatementWithColumnNames implements StatementBuilder {
    410        @Override
    411        public Statement createStatement(final Connection connection) throws SQLException {
    412            final PreparedStatement statement = connection.prepareStatement(
    413                    _sql, _columnNames);
    414            return statement;
    415        }
    416    }
    417
    418    /**
    419     * Builder for prepareCall(String sql)
    420     */
    421    private class PreparedCallSQL implements StatementBuilder {
    422        @Override
    423        public Statement createStatement(final Connection connection) throws SQLException {
    424            final PreparedStatement statement = connection.prepareCall(_sql);
    425            return statement;
    426        }
    427    }
    428
    429    /**
    430     * Builder for prepareCall(String sql, int resultSetType, int resultSetConcurrency)
    431     */
    432    private class PreparedCallWithResultSetConcurrency implements StatementBuilder {
    433        @Override
    434        public Statement createStatement(final Connection connection) throws SQLException {
    435            final PreparedStatement statement = connection.prepareCall(
    436                    _sql, _resultSetType.intValue(), _resultSetConcurrency.intValue());
    437            return statement;
    438        }
    439    }
    440
    441    /**
    442     * Builder for prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
    443     */
    444    private class PreparedCallWithResultSetHoldability implements StatementBuilder {
    445        @Override
    446        public Statement createStatement(final Connection connection) throws SQLException {
    447            final PreparedStatement statement = connection.prepareCall(
    448                    _sql, _resultSetType.intValue(), _resultSetConcurrency.intValue(),
    449                    _resultSetHoldability.intValue());
    450            return statement;
    451        }
    452    }
    453}