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