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