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