    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 *
    030 * @since 2.0
    031 */
    032public class PStmtKey {
    033
    034    /**
    035     * SQL defining Prepared or Callable Statement
    036     */
    037    private final String sql;
    038
    039    /**
    040     * Result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>, <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>,
    041     * or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    042     */
    043    private final Integer resultSetType;
    044
    045    /**
    046     * Result set concurrency. A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    047     * <code>ResultSet.CONCUR_UPDATABLE</code>.
    048     */
    049    private final Integer resultSetConcurrency;
    050
    051    /**
    052     * Result set holdability. One of the following <code>ResultSet</code> constants:
    053     * <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
    054     */
    055    private final Integer resultSetHoldability;
    056
    057    /** Database catalog */
    058    private final String catalog;
    059
    060    /**
    061     * A flag indicating whether auto-generated keys should be returned; one of
    062     * <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
    063     */
    064    private final Integer autoGeneratedKeys;
    065
    066    /**
    067     * An array of column indexes indicating the columns that should be returned from the inserted row or rows.
    068     */
    069    private final int[] columnIndexes;
    070
    071    /**
    072     * An array of column names indicating the columns that should be returned from the inserted row or rows.
    073     */
    074    private final String[] columnNames;
    075
    076    /**
    077     * Statement type, prepared or callable.
    078     */
    079    private final StatementType statementType;
    080
    081    /** Statement builder */
    082    private StatementBuilder builder;
    083
    084    /**
    085     * Constructs a key to uniquely identify a prepared statement.
    086     *
    087     * @param sql
    088     *            The SQL statement.
    089     */
    090    public PStmtKey(final String sql) {
    091        this(sql, null, StatementType.PREPARED_STATEMENT);
    092    }
    093
    094    /**
    095     * Constructs a key to uniquely identify a prepared statement.
    096     *
    097     * @param sql
    098     *            The SQL statement.
    099     * @param catalog
    100     *            The catalog.
    101     */
    102    public PStmtKey(final String sql, final String catalog) {
    103        this(sql, catalog, StatementType.PREPARED_STATEMENT);
    104    }
    105
    106    /**
    107     * Constructs a key to uniquely identify a prepared statement.
    108     *
    109     * @param sql
    110     *            The SQL statement.
    111     * @param catalog
    112     *            The catalog.
    113     * @param statementType
    114     *            The SQL statement type, prepared or callable.
    115     */
    116    public PStmtKey(final String sql, final String catalog, final StatementType statementType) {
    117        this.sql = sql;
    118        this.catalog = catalog;
    119        this.statementType = statementType;
    120        this.autoGeneratedKeys = null;
    121        this.columnIndexes = null;
    122        this.columnNames = null;
    123        this.resultSetType = null;
    124        this.resultSetConcurrency = null;
    125        this.resultSetHoldability = null;
    126        // create builder
    127        if (statementType == StatementType.PREPARED_STATEMENT) {
    128            this.builder = new PreparedStatementSQL();
    129        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
    130            this.builder = new PreparedCallSQL();
    131        }
    132    }
    133
    134    /**
    135     * Constructs a key to uniquely identify a prepared statement.
    136     *
    137     * @param sql
    138     *            The SQL statement.
    139     * @param catalog
    140     *            The catalog.
    141     * @param autoGeneratedKeys
    142     *            A flag indicating whether auto-generated keys should be returned; one of
    143     *            <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
    144     */
    145    public PStmtKey(final String sql, final String catalog, final int autoGeneratedKeys) {
    146        this(sql, catalog, StatementType.PREPARED_STATEMENT, Integer.valueOf(autoGeneratedKeys));
    147    }
    148
    149    /**
    150     * Constructs a key to uniquely identify a prepared statement.
    151     *
    152     * @param sql
    153     *            The SQL statement.
    154     * @param catalog
    155     *            The catalog.
    156     * @param statementType
    157     *            The SQL statement type, prepared or callable.
    158     * @param autoGeneratedKeys
    159     *            A flag indicating whether auto-generated keys should be returned; one of
    160     *            <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
    161     */
    162    public PStmtKey(final String sql, final String catalog, final StatementType statementType,
    163            final Integer autoGeneratedKeys) {
    164        this.sql = sql;
    165        this.catalog = catalog;
    166        this.statementType = statementType;
    167        this.autoGeneratedKeys = autoGeneratedKeys;
    168        this.columnIndexes = null;
    169        this.columnNames = null;
    170        this.resultSetType = null;
    171        this.resultSetConcurrency = null;
    172        this.resultSetHoldability = null;
    173        // create builder
    174        if (statementType == StatementType.PREPARED_STATEMENT) {
    175            this.builder = new PreparedStatementWithAutoGeneratedKeys();
    176        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
    177            this.builder = new PreparedCallSQL();
    178        }
    179    }
    180
    181    /**
    182     * Constructs a key to uniquely identify a prepared statement.
    183     *
    184     * @param sql
    185     *            The SQL statement.
    186     * @param catalog
    187     *            The catalog.
    188     * @param columnIndexes
    189     *            An array of column indexes indicating the columns that should be returned from the inserted row or
    190     *            rows.
    191     */
    192    public PStmtKey(final String sql, final String catalog, final int[] columnIndexes) {
    193        this.sql = sql;
    194        this.catalog = catalog;
    195        this.statementType = StatementType.PREPARED_STATEMENT;
    196        this.autoGeneratedKeys = null;
    197        this.columnIndexes = columnIndexes == null ? null : Arrays.copyOf(columnIndexes, columnIndexes.length);
    198        this.columnNames = null;
    199        this.resultSetType = null;
    200        this.resultSetConcurrency = null;
    201        this.resultSetHoldability = null;
    202        // create builder
    203        this.builder = new PreparedStatementWithColumnIndexes();
    204    }
    205
    206    /**
    207     * Constructs a key to uniquely identify a prepared statement.
    208     *
    209     * @param sql
    210     *            The SQL statement.
    211     * @param catalog
    212     *            The catalog.
    213     * @param columnNames
    214     *            An array of column names indicating the columns that should be returned from the inserted row or rows.
    215     */
    216    public PStmtKey(final String sql, final String catalog, final String[] columnNames) {
    217        this.sql = sql;
    218        this.catalog = catalog;
    219        this.statementType = StatementType.PREPARED_STATEMENT;
    220        this.autoGeneratedKeys = null;
    221        this.columnIndexes = null;
    222        this.columnNames = columnNames == null ? null : Arrays.copyOf(columnNames, columnNames.length);
    223        this.resultSetType = null;
    224        this.resultSetConcurrency = null;
    225        this.resultSetHoldability = null;
    226        // create builder
    227        builder = new PreparedStatementWithColumnNames();
    228    }
    229
    230    /**
    231     * Constructs a key to uniquely identify a prepared statement.
    232     *
    233     * @param sql
    234     *            The SQL statement.
    235     * @param resultSetType
    236     *            A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    237     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    238     * @param resultSetConcurrency
    239     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    240     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
    241     */
    242    public PStmtKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
    243        this(sql, null, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
    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     */
    260    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency) {
    261        this(sql, catalog, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT);
    262    }
    263
    264    /**
    265     * Constructs a key to uniquely identify a prepared statement.
    266     *
    267     * @param sql
    268     *            The SQL statement.
    269     * @param catalog
    270     *            The catalog.
    271     * @param resultSetType
    272     *            A result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    273     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    274     * @param resultSetConcurrency
    275     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    276     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
    277     * @param statementType
    278     *            The SQL statement type, prepared or callable.
    279     */
    280    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
    281            final StatementType statementType) {
    282        this.sql = sql;
    283        this.catalog = catalog;
    284        this.resultSetType = Integer.valueOf(resultSetType);
    285        this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
    286        this.resultSetHoldability = null;
    287        this.statementType = statementType;
    288        this.autoGeneratedKeys = null;
    289        this.columnIndexes = null;
    290        this.columnNames = null;
    291        // create builder
    292        if (statementType == StatementType.PREPARED_STATEMENT) {
    293            this.builder = new PreparedStatementWithResultSetConcurrency();
    294        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
    295            this.builder = new PreparedCallWithResultSetConcurrency();
    296        }
    297    }
    298
    299    /**
    300     * Constructs a key to uniquely identify a prepared statement.
    301     *
    302     * @param sql
    303     *            The SQL statement.
    304     * @param catalog
    305     *            The catalog.
    306     * @param resultSetType
    307     *            a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    308     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    309     * @param resultSetConcurrency
    310     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    311     *            <code>ResultSet.CONCUR_UPDATABLE</code>
    312     * @param resultSetHoldability
    313     *            One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
    314     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
    315     */
    316    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
    317            final int resultSetHoldability) {
    318        this(sql, catalog, resultSetType, resultSetConcurrency, resultSetHoldability, StatementType.PREPARED_STATEMENT);
    319    }
    320
    321    /**
    322     * Constructs a key to uniquely identify a prepared statement.
    323     *
    324     * @param sql
    325     *            The SQL statement.
    326     * @param catalog
    327     *            The catalog.
    328     * @param resultSetType
    329     *            a result set type; one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    330     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
    331     * @param resultSetConcurrency
    332     *            A concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    333     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
    334     * @param resultSetHoldability
    335     *            One of the following <code>ResultSet</code> constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
    336     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
    337     * @param statementType
    338     *            The SQL statement type, prepared or callable.
    339     */
    340    public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency,
    341            final int resultSetHoldability, final StatementType statementType) {
    342        this.sql = sql;
    343        this.catalog = catalog;
    344        this.resultSetType = Integer.valueOf(resultSetType);
    345        this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
    346        this.resultSetHoldability = Integer.valueOf(resultSetHoldability);
    347        this.statementType = statementType;
    348        this.autoGeneratedKeys = null;
    349        this.columnIndexes = null;
    350        this.columnNames = null;
    351        // create builder
    352        if (statementType == StatementType.PREPARED_STATEMENT) {
    353            this.builder = new PreparedStatementWithResultSetHoldability();
    354        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
    355            this.builder = new PreparedCallWithResultSetHoldability();
    356        }
    357    }
    358
    359    /**
    360     * Gets the SQL statement.
    361     *
    362     * @return the SQL statement.
    363     */
    364    public String getSql() {
    365        return sql;
    366    }
    367
    368    /**
    369     * Gets the result set type, one of <code>ResultSet.TYPE_FORWARD_ONLY</code>,
    370     * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
    371     *
    372     * @return the result set type.
    373     */
    374    public Integer getResultSetType() {
    375        return resultSetType;
    376    }
    377
    378    /**
    379     * Gets the result set concurrency type; one of <code>ResultSet.CONCUR_READ_ONLY</code> or
    380     * <code>ResultSet.CONCUR_UPDATABLE</code>.
    381     *
    382     * @return The result set concurrency type.
    383     */
    384    public Integer getResultSetConcurrency() {
    385        return resultSetConcurrency;
    386    }
    387
    388    /**
    389     * Gets the result set holdability, one of the following <code>ResultSet</code> constants:
    390     * <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
    391     *
    392     * @return The result set holdability.
    393     */
    394    public Integer getResultSetHoldability() {
    395        return resultSetHoldability;
    396    }
    397
    398    /**
    399     * Gets a flag indicating whether auto-generated keys should be returned; one of
    400     * <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>.
    401     *
    402     * @return a flag indicating whether auto-generated keys should be returned.
    403     */
    404    public Integer getAutoGeneratedKeys() {
    405        return autoGeneratedKeys;
    406    }
    407
    408    /**
    409     * Gets an array of column indexes indicating the columns that should be returned from the inserted row or rows.
    410     *
    411     * @return An array of column indexes.
    412     */
    413    public int[] getColumnIndexes() {
    414        return columnIndexes;
    415    }
    416
    417    /**
    418     * Gets an array of column names indicating the columns that should be returned from the inserted row or rows.
    419     *
    420     * @return An array of column names.
    421     */
    422    public String[] getColumnNames() {
    423        return columnNames;
    424    }
    425
    426    /**
    427     * The catalog.
    428     *
    429     * @return The catalog.
    430     */
    431    public String getCatalog() {
    432        return catalog;
    433    }
    434
    435    /**
    436     * The SQL statement type.
    437     *
    438     * @return The SQL statement type.
    439     */
    440    public StatementType getStmtType() {
    441        return statementType;
    442    }
    443
    444    @Override
    445    public boolean equals(final Object obj) {
    446        if (this == obj) {
    447            return true;
    448        }
    449        if (obj == null) {
    450            return false;
    451        }
    452        if (getClass() != obj.getClass()) {
    453            return false;
    454        }
    455        final PStmtKey other = (PStmtKey) obj;
    456        if (catalog == null) {
    457            if (other.catalog != null) {
    458                return false;
    459            }
    460        } else if (!catalog.equals(other.catalog)) {
    461            return false;
    462        }
    463        if (resultSetConcurrency == null) {
    464            if (other.resultSetConcurrency != null) {
    465                return false;
    466            }
    467        } else if (!resultSetConcurrency.equals(other.resultSetConcurrency)) {
    468            return false;
    469        }
    470        if (resultSetType == null) {
    471            if (other.resultSetType != null) {
    472                return false;
    473            }
    474        } else if (!resultSetType.equals(other.resultSetType)) {
    475            return false;
    476        }
    477        if (resultSetHoldability == null) {
    478            if (other.resultSetHoldability != null) {
    479                return false;
    480            }
    481        } else if (!resultSetHoldability.equals(other.resultSetHoldability)) {
    482            return false;
    483        }
    484        if (autoGeneratedKeys == null) {
    485            if (other.autoGeneratedKeys != null) {
    486                return false;
    487            }
    488        } else if (!autoGeneratedKeys.equals(other.autoGeneratedKeys)) {
    489            return false;
    490        }
    491        if (!Arrays.equals(columnIndexes, other.columnIndexes)) {
    492            return false;
    493        }
    494        if (!Arrays.equals(columnNames, other.columnNames)) {
    495            return false;
    496        }
    497        if (sql == null) {
    498            if (other.sql != null) {
    499                return false;
    500            }
    501        } else if (!sql.equals(other.sql)) {
    502            return false;
    503        }
    504        if (statementType != other.statementType) {
    505            return false;
    506        }
    507        return true;
    508    }
    509
    510    @Override
    511    public int hashCode() {
    512        final int prime = 31;
    513        int result = 1;
    514        result = prime * result + (catalog == null ? 0 : catalog.hashCode());
    515        result = prime * result + (resultSetConcurrency == null ? 0 : resultSetConcurrency.hashCode());
    516        result = prime * result + (resultSetType == null ? 0 : resultSetType.hashCode());
    517        result = prime * result + (resultSetHoldability == null ? 0 : resultSetHoldability.hashCode());
    518        result = prime * result + (sql == null ? 0 : sql.hashCode());
    519        result = prime * result + (autoGeneratedKeys == null ? 0 : autoGeneratedKeys.hashCode());
    520        result = prime * result + Arrays.hashCode(columnIndexes);
    521        result = prime * result + Arrays.hashCode(columnNames);
    522        result = prime * result + statementType.hashCode();
    523        return result;
    524    }
    525
    526    @Override
    527    public String toString() {
    528        final StringBuffer buf = new StringBuffer();
    529        buf.append("PStmtKey: sql=");
    530        buf.append(sql);
    531        buf.append(", catalog=");
    532        buf.append(catalog);
    533        buf.append(", resultSetType=");
    534        buf.append(resultSetType);
    535        buf.append(", resultSetConcurrency=");
    536        buf.append(resultSetConcurrency);
    537        buf.append(", resultSetHoldability=");
    538        buf.append(resultSetHoldability);
    539        buf.append(", autoGeneratedKeys=");
    540        buf.append(autoGeneratedKeys);
    541        buf.append(", columnIndexes=");
    542        buf.append(Arrays.toString(columnIndexes));
    543        buf.append(", columnNames=");
    544        buf.append(Arrays.toString(columnNames));
    545        buf.append(", statementType=");
    546        buf.append(statementType);
    547        return buf.toString();
    548    }
    549
    550    /**
    551     * Creates a new Statement from the given Connection.
    552     *
    553     * @param connection
    554     *            The Connection to use to create the statement.
    555     * @return The statement.
    556     * @throws SQLException
    557     *             Thrown when there is a problem creating the statement.
    558     */
    559    public Statement createStatement(final Connection connection) throws SQLException {
    560        if (builder == null) {
    561            throw new IllegalStateException("Prepared statement key is invalid.");
    562        }
    563        return builder.createStatement(connection);
    564    }
    565
    566    /**
    567     * Interface for Prepared or Callable Statement.
    568     */
    569    private interface StatementBuilder {
    570        public Statement createStatement(Connection connection) throws SQLException;
    571    }
    572
    573    /**
    574     * Builder for prepareStatement(String sql).
    575     */
    576    private class PreparedStatementSQL implements StatementBuilder {
    577        @Override
    578        public Statement createStatement(final Connection connection) throws SQLException {
    579            final PreparedStatement statement = connection.prepareStatement(sql);
    580            return statement;
    581        }
    582    }
    583
    584    /**
    585     * Builder for prepareStatement(String sql, int autoGeneratedKeys).
    586     */
    587    private class PreparedStatementWithAutoGeneratedKeys implements StatementBuilder {
    588        @Override
    589        public Statement createStatement(final Connection connection) throws SQLException {
    590            final PreparedStatement statement = connection.prepareStatement(sql, autoGeneratedKeys.intValue());
    591            return statement;
    592        }
    593    }
    594
    595    /**
    596     * Builder for prepareStatement(String sql, int[] columnIndexes).
    597     */
    598    private class PreparedStatementWithColumnIndexes implements StatementBuilder {
    599        @Override
    600        public Statement createStatement(final Connection connection) throws SQLException {
    601            final PreparedStatement statement = connection.prepareStatement(sql, columnIndexes);
    602            return statement;
    603        }
    604    }
    605
    606    /**
    607     * Builder for prepareStatement(String sql, int resultSetType, int resultSetConcurrency).
    608     */
    609    private class PreparedStatementWithResultSetConcurrency implements StatementBuilder {
    610        @Override
    611        public Statement createStatement(final Connection connection) throws SQLException {
    612            final PreparedStatement statement = connection.prepareStatement(sql, resultSetType.intValue(),
    613                    resultSetConcurrency.intValue());
    614            return statement;
    615        }
    616    }
    617
    618    /**
    619     * Builder for prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability).
    620     */
    621    private class PreparedStatementWithResultSetHoldability implements StatementBuilder {
    622        @Override
    623        public Statement createStatement(final Connection connection) throws SQLException {
    624            final PreparedStatement statement = connection.prepareStatement(sql, resultSetType.intValue(),
    625                    resultSetConcurrency.intValue(), resultSetHoldability.intValue());
    626            return statement;
    627        }
    628    }
    629
    630    /**
    631     * Builder for prepareStatement(String sql, String[] columnNames).
    632     */
    633    private class PreparedStatementWithColumnNames implements StatementBuilder {
    634        @Override
    635        public Statement createStatement(final Connection connection) throws SQLException {
    636            final PreparedStatement statement = connection.prepareStatement(sql, columnNames);
    637            return statement;
    638        }
    639    }
    640
    641    /**
    642     * Builder for prepareCall(String sql).
    643     */
    644    private class PreparedCallSQL implements StatementBuilder {
    645        @Override
    646        public Statement createStatement(final Connection connection) throws SQLException {
    647            final PreparedStatement statement = connection.prepareCall(sql);
    648            return statement;
    649        }
    650    }
    651
    652    /**
    653     * Builder for prepareCall(String sql, int resultSetType, int resultSetConcurrency).
    654     */
    655    private class PreparedCallWithResultSetConcurrency implements StatementBuilder {
    656        @Override
    657        public Statement createStatement(final Connection connection) throws SQLException {
    658            final PreparedStatement statement = connection.prepareCall(sql, resultSetType.intValue(),
    659                    resultSetConcurrency.intValue());
    660            return statement;
    661        }
    662    }
    663
    664    /**
    665     * Builder for prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability).
    666     */
    667    private class PreparedCallWithResultSetHoldability implements StatementBuilder {
    668        @Override
    669        public Statement createStatement(final Connection connection) throws SQLException {
    670            final PreparedStatement statement = connection.prepareCall(sql, resultSetType.intValue(),
    671                    resultSetConcurrency.intValue(), resultSetHoldability.intValue());
    672            return statement;
    673        }
    674    }
    675}