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