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}