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.CallableStatement; 020import java.sql.Connection; 021import java.sql.PreparedStatement; 022import java.sql.SQLException; 023import java.util.NoSuchElementException; 024import java.util.Objects; 025 026import org.apache.commons.pool2.KeyedObjectPool; 027import org.apache.commons.pool2.KeyedPooledObjectFactory; 028import org.apache.commons.pool2.PooledObject; 029import org.apache.commons.pool2.impl.DefaultPooledObject; 030 031/** 032 * A {@link DelegatingConnection} that pools {@link PreparedStatement}s. 033 * <p> 034 * The {@link #prepareStatement} and {@link #prepareCall} methods, rather than creating a new PreparedStatement each 035 * time, may actually pull the statement from a pool of unused statements. The {@link PreparedStatement#close} method of 036 * the returned statement doesn't actually close the statement, but rather returns it to the pool. (See 037 * {@link PoolablePreparedStatement}, {@link PoolableCallableStatement}.) 038 * </p> 039 * 040 * @see PoolablePreparedStatement 041 * @since 2.0 042 */ 043public class PoolingConnection extends DelegatingConnection<Connection> 044 implements KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> { 045 046 /** 047 * Statement types. 048 * 049 * @since 2.0 protected enum. 050 * @since 2.4.0 public enum. 051 */ 052 public enum StatementType { 053 054 /** 055 * Callable statement. 056 */ 057 CALLABLE_STATEMENT, 058 059 /** 060 * Prepared statement. 061 */ 062 PREPARED_STATEMENT 063 } 064 065 /** Pool of {@link PreparedStatement}s. and {@link CallableStatement}s */ 066 private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pStmtPool; 067 068 private boolean clearStatementPoolOnReturn; 069 070 /** 071 * Constructor. 072 * 073 * @param connection 074 * the underlying {@link Connection}. 075 */ 076 public PoolingConnection(final Connection connection) { 077 super(connection); 078 } 079 080 /** 081 * {@link KeyedPooledObjectFactory} method for activating pooled statements. 082 * 083 * @param key 084 * ignored 085 * @param pooledObject 086 * wrapped pooled statement to be activated 087 */ 088 @Override 089 public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) 090 throws SQLException { 091 pooledObject.getObject().activate(); 092 } 093 094 /** 095 * Closes and frees all {@link PreparedStatement}s or {@link CallableStatement}s from the pool, and close the 096 * underlying connection. 097 */ 098 @Override 099 public synchronized void close() throws SQLException { 100 try { 101 if (null != pStmtPool) { 102 final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> oldPool = pStmtPool; 103 pStmtPool = null; 104 try { 105 oldPool.close(); 106 } catch (final RuntimeException e) { 107 throw e; 108 } catch (final Exception e) { 109 throw new SQLException("Cannot close connection", e); 110 } 111 } 112 } finally { 113 try { 114 getDelegateInternal().close(); 115 } finally { 116 setClosedInternal(true); 117 } 118 } 119 } 120 121 /** 122 * Notification from {@link PoolableConnection} that we returned to the pool. 123 * 124 * @throws SQLException when {@code clearStatementPoolOnReturn} is true and the statement pool could not be 125 * cleared 126 * @since 2.8.0 127 */ 128 public void connectionReturnedToPool() throws SQLException { 129 if (pStmtPool != null && clearStatementPoolOnReturn) { 130 try { 131 pStmtPool.clear(); 132 } catch (final Exception e) { 133 throw new SQLException("Error clearing statement pool", e); 134 } 135 } 136 } 137 138 /** 139 * Creates a PStmtKey for the given arguments. 140 * 141 * @param sql 142 * the SQL string used to define the statement 143 * 144 * @return the PStmtKey created for the given arguments. 145 */ 146 protected PStmtKey createKey(final String sql) { 147 return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull()); 148 } 149 150 /** 151 * Creates a PStmtKey for the given arguments. 152 * 153 * @param sql 154 * the SQL string used to define the statement 155 * @param autoGeneratedKeys 156 * A flag indicating whether auto-generated keys should be returned; one of 157 * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}. 158 * 159 * @return the PStmtKey created for the given arguments. 160 */ 161 protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) { 162 return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), autoGeneratedKeys); 163 } 164 165 /** 166 * Creates a PStmtKey for the given arguments. 167 * 168 * @param sql 169 * the SQL string used to define the statement 170 * @param resultSetType 171 * result set type 172 * @param resultSetConcurrency 173 * result set concurrency 174 * 175 * @return the PStmtKey created for the given arguments. 176 */ 177 protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) { 178 return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency); 179 } 180 181 /** 182 * Creates a PStmtKey for the given arguments. 183 * 184 * @param sql 185 * the SQL string used to define the statement 186 * @param resultSetType 187 * result set type 188 * @param resultSetConcurrency 189 * result set concurrency 190 * @param resultSetHoldability 191 * result set holdability 192 * 193 * @return the PStmtKey created for the given arguments. 194 */ 195 protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, 196 final int resultSetHoldability) { 197 return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, 198 resultSetHoldability); 199 } 200 201 /** 202 * Creates a PStmtKey for the given arguments. 203 * 204 * @param sql 205 * the SQL string used to define the statement 206 * @param resultSetType 207 * result set type 208 * @param resultSetConcurrency 209 * result set concurrency 210 * @param resultSetHoldability 211 * result set holdability 212 * @param statementType 213 * statement type 214 * 215 * @return the PStmtKey created for the given arguments. 216 */ 217 protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, 218 final int resultSetHoldability, final StatementType statementType) { 219 return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, 220 resultSetHoldability, statementType); 221 } 222 223 /** 224 * Creates a PStmtKey for the given arguments. 225 * 226 * @param sql 227 * the SQL string used to define the statement 228 * @param resultSetType 229 * result set type 230 * @param resultSetConcurrency 231 * result set concurrency 232 * @param statementType 233 * statement type 234 * 235 * @return the PStmtKey created for the given arguments. 236 */ 237 protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, 238 final StatementType statementType) { 239 return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, statementType); 240 } 241 242 /** 243 * Creates a PStmtKey for the given arguments. 244 * 245 * @param sql 246 * the SQL string used to define the statement 247 * @param columnIndexes 248 * An array of column indexes indicating the columns that should be returned from the inserted row or 249 * rows. 250 * 251 * @return the PStmtKey created for the given arguments. 252 */ 253 protected PStmtKey createKey(final String sql, final int[] columnIndexes) { 254 return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnIndexes); 255 } 256 257 /** 258 * Creates a PStmtKey for the given arguments. 259 * 260 * @param sql 261 * the SQL string used to define the statement 262 * @param statementType 263 * statement type 264 * 265 * @return the PStmtKey created for the given arguments. 266 */ 267 protected PStmtKey createKey(final String sql, final StatementType statementType) { 268 return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), statementType, null); 269 } 270 271 /** 272 * Creates a PStmtKey for the given arguments. 273 * 274 * @param sql 275 * the SQL string used to define the statement 276 * @param columnNames 277 * column names 278 * 279 * @return the PStmtKey created for the given arguments. 280 */ 281 protected PStmtKey createKey(final String sql, final String[] columnNames) { 282 return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), getSchemaOrNull(), columnNames); 283 } 284 285 /** 286 * {@link KeyedPooledObjectFactory} method for destroying PoolablePreparedStatements and PoolableCallableStatements. 287 * Closes the underlying statement. 288 * 289 * @param key 290 * ignored 291 * @param pooledObject 292 * the wrapped pooled statement to be destroyed. 293 */ 294 @Override 295 public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) 296 throws SQLException { 297 pooledObject.getObject().getInnermostDelegate().close(); 298 } 299 300 private String getCatalogOrNull() { 301 try { 302 return getCatalog(); 303 } catch (final SQLException ignored) { 304 return null; 305 } 306 } 307 308 private String getSchemaOrNull() { 309 try { 310 return getSchema(); 311 } catch (final SQLException ignored) { 312 return null; 313 } 314 } 315 316 /** 317 * Gets the prepared statement pool. 318 * 319 * @return statement pool 320 * @since 2.8.0 321 */ 322 public KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> getStatementPool() { 323 return pStmtPool; 324 } 325 326 /** 327 * {@link KeyedPooledObjectFactory} method for creating {@link PoolablePreparedStatement}s or 328 * {@link PoolableCallableStatement}s. The {@code stmtType} field in the key determines whether a 329 * PoolablePreparedStatement or PoolableCallableStatement is created. 330 * 331 * @param key 332 * the key for the {@link PreparedStatement} to be created 333 * @see #createKey(String, int, int, StatementType) 334 */ 335 @SuppressWarnings("resource") 336 @Override 337 public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws SQLException { 338 if (null == key) { 339 throw new IllegalArgumentException("Prepared statement key is null or invalid."); 340 } 341 if (key.getStmtType() == StatementType.PREPARED_STATEMENT) { 342 final PreparedStatement statement = (PreparedStatement) key.createStatement(getDelegate()); 343 @SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this 344 final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pStmtPool, this); 345 return new DefaultPooledObject<>(pps); 346 } 347 final CallableStatement statement = (CallableStatement) key.createStatement(getDelegate()); 348 final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pStmtPool, this); 349 return new DefaultPooledObject<>(pcs); 350 } 351 352 /** 353 * Normalizes the given SQL statement, producing a canonical form that is semantically equivalent to the original. 354 * 355 * @param sql The statement to be normalized. 356 * 357 * @return The canonical form of the supplied SQL statement. 358 */ 359 protected String normalizeSQL(final String sql) { 360 return sql.trim(); 361 } 362 363 /** 364 * {@link KeyedPooledObjectFactory} method for passivating {@link PreparedStatement}s or {@link CallableStatement}s. 365 * Invokes {@link PreparedStatement#clearParameters}. 366 * 367 * @param key 368 * ignored 369 * @param pooledObject 370 * a wrapped {@link PreparedStatement} 371 */ 372 @Override 373 public void passivateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) 374 throws SQLException { 375 @SuppressWarnings("resource") 376 final DelegatingPreparedStatement dps = pooledObject.getObject(); 377 dps.clearParameters(); 378 dps.passivate(); 379 } 380 381 /** 382 * Creates or obtains a {@link CallableStatement} from the pool. 383 * 384 * @param key 385 * a {@link PStmtKey} for the given arguments 386 * @return a {@link PoolableCallableStatement} 387 * @throws SQLException 388 * Wraps an underlying exception. 389 */ 390 private CallableStatement prepareCall(final PStmtKey key) throws SQLException { 391 return (CallableStatement) prepareStatement(key); 392 } 393 394 /** 395 * Creates or obtains a {@link CallableStatement} from the pool. 396 * 397 * @param sql 398 * the SQL string used to define the CallableStatement 399 * @return a {@link PoolableCallableStatement} 400 * @throws SQLException 401 * Wraps an underlying exception. 402 */ 403 @Override 404 public CallableStatement prepareCall(final String sql) throws SQLException { 405 return prepareCall(createKey(sql, StatementType.CALLABLE_STATEMENT)); 406 } 407 408 /** 409 * Creates or obtains a {@link CallableStatement} from the pool. 410 * 411 * @param sql 412 * the SQL string used to define the CallableStatement 413 * @param resultSetType 414 * result set type 415 * @param resultSetConcurrency 416 * result set concurrency 417 * @return a {@link PoolableCallableStatement} 418 * @throws SQLException 419 * Wraps an underlying exception. 420 */ 421 @Override 422 public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency) 423 throws SQLException { 424 return prepareCall(createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT)); 425 } 426 427 /** 428 * Creates or obtains a {@link CallableStatement} from the pool. 429 * 430 * @param sql 431 * the SQL string used to define the CallableStatement 432 * @param resultSetType 433 * result set type 434 * @param resultSetConcurrency 435 * result set concurrency 436 * @param resultSetHoldability 437 * result set holdability 438 * @return a {@link PoolableCallableStatement} 439 * @throws SQLException 440 * Wraps an underlying exception. 441 */ 442 @Override 443 public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency, 444 final int resultSetHoldability) throws SQLException { 445 return prepareCall(createKey(sql, resultSetType, resultSetConcurrency, 446 resultSetHoldability, StatementType.CALLABLE_STATEMENT)); 447 } 448 449 /** 450 * Creates or obtains a {@link PreparedStatement} from the pool. 451 * 452 * @param key 453 * a {@link PStmtKey} for the given arguments 454 * @return a {@link PoolablePreparedStatement} 455 * @throws SQLException 456 * Wraps an underlying exception. 457 */ 458 private PreparedStatement prepareStatement(final PStmtKey key) throws SQLException { 459 if (null == pStmtPool) { 460 throw new SQLException("Statement pool is null - closed or invalid PoolingConnection."); 461 } 462 try { 463 return pStmtPool.borrowObject(key); 464 } catch (final NoSuchElementException e) { 465 throw new SQLException("MaxOpenPreparedStatements limit reached", e); 466 } catch (final RuntimeException e) { 467 throw e; 468 } catch (final Exception e) { 469 throw new SQLException("Borrow prepareStatement from pool failed", e); 470 } 471 } 472 473 /** 474 * Creates or obtains a {@link PreparedStatement} from the pool. 475 * 476 * @param sql 477 * the SQL string used to define the PreparedStatement 478 * @return a {@link PoolablePreparedStatement} 479 * @throws SQLException 480 * Wraps an underlying exception. 481 */ 482 @Override 483 public PreparedStatement prepareStatement(final String sql) throws SQLException { 484 return prepareStatement(createKey(sql)); 485 } 486 487 /* 488 * Creates or obtains a {@link PreparedStatement} from the pool. 489 * 490 * @param sql 491 * the SQL string used to define the PreparedStatement 492 * @param autoGeneratedKeys 493 * A flag indicating whether auto-generated keys should be returned; one of 494 * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}. 495 * @return a {@link PoolablePreparedStatement} 496 * @throws SQLException 497 * Wraps an underlying exception. 498 */ 499 @Override 500 public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException { 501 return prepareStatement(createKey(sql, autoGeneratedKeys)); 502 } 503 504 /** 505 * Creates or obtains a {@link PreparedStatement} from the pool. 506 * 507 * @param sql 508 * the SQL string used to define the PreparedStatement 509 * @param resultSetType 510 * result set type 511 * @param resultSetConcurrency 512 * result set concurrency 513 * @return a {@link PoolablePreparedStatement} 514 * @throws SQLException 515 * Wraps an underlying exception. 516 */ 517 @Override 518 public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency) 519 throws SQLException { 520 return prepareStatement(createKey(sql, resultSetType, resultSetConcurrency)); 521 } 522 523 /** 524 * Creates or obtains a {@link PreparedStatement} from the pool. 525 * 526 * @param sql 527 * the SQL string used to define the PreparedStatement 528 * @param resultSetType 529 * result set type 530 * @param resultSetConcurrency 531 * result set concurrency 532 * @param resultSetHoldability 533 * result set holdability 534 * @return a {@link PoolablePreparedStatement} 535 * @throws SQLException 536 * Wraps an underlying exception. 537 */ 538 @Override 539 public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency, 540 final int resultSetHoldability) throws SQLException { 541 return prepareStatement(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); 542 } 543 544 /** 545 * Creates or obtains a {@link PreparedStatement} from the pool. 546 * 547 * @param sql 548 * the SQL string used to define the PreparedStatement 549 * @param columnIndexes 550 * An array of column indexes indicating the columns that should be returned from the inserted row or 551 * rows. 552 * @return a {@link PoolablePreparedStatement} 553 * @throws SQLException 554 * Wraps an underlying exception. 555 * 556 */ 557 @Override 558 public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException { 559 return prepareStatement(createKey(sql, columnIndexes)); 560 } 561 562 /** 563 * Creates or obtains a {@link PreparedStatement} from the pool. 564 * 565 * @param sql 566 * the SQL string used to define the PreparedStatement 567 * @param columnNames 568 * column names 569 * @return a {@link PoolablePreparedStatement} 570 * @throws SQLException 571 * Wraps an underlying exception. 572 */ 573 @Override 574 public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException { 575 return prepareStatement(createKey(sql, columnNames)); 576 } 577 578 /** 579 * Sets whether the pool of statements should be cleared when the connection is returned to its pool. 580 * Default is false. 581 * 582 * @param clearStatementPoolOnReturn clear or not 583 * @since 2.8.0 584 */ 585 public void setClearStatementPoolOnReturn(final boolean clearStatementPoolOnReturn) { 586 this.clearStatementPoolOnReturn = clearStatementPoolOnReturn; 587 } 588 589 /** 590 * Sets the prepared statement pool. 591 * 592 * @param pool 593 * the prepared statement pool. 594 */ 595 public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pool) { 596 pStmtPool = pool; 597 } 598 599 @Override 600 public synchronized String toString() { 601 return "PoolingConnection: " + Objects.toString(pStmtPool); 602 } 603 604 /** 605 * {@link KeyedPooledObjectFactory} method for validating pooled statements. Currently, always returns true. 606 * 607 * @param key 608 * ignored 609 * @param pooledObject 610 * ignored 611 * @return {@code true} 612 */ 613 @Override 614 public boolean validateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) { 615 return true; 616 } 617}