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