1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.dbcp2.cpdsadapter;
18
19 import java.sql.CallableStatement;
20 import java.sql.Connection;
21 import java.sql.PreparedStatement;
22 import java.sql.SQLException;
23 import java.sql.Statement;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.List;
27
28 import javax.sql.ConnectionEvent;
29 import javax.sql.ConnectionEventListener;
30 import javax.sql.PooledConnection;
31 import javax.sql.StatementEventListener;
32
33 import org.apache.commons.dbcp2.DelegatingConnection;
34 import org.apache.commons.dbcp2.DelegatingPreparedStatement;
35 import org.apache.commons.dbcp2.Jdbc41Bridge;
36 import org.apache.commons.dbcp2.PStmtKey;
37 import org.apache.commons.dbcp2.PoolableCallableStatement;
38 import org.apache.commons.dbcp2.PoolablePreparedStatement;
39 import org.apache.commons.dbcp2.PoolingConnection.StatementType;
40 import org.apache.commons.dbcp2.Utils;
41 import org.apache.commons.pool2.KeyedObjectPool;
42 import org.apache.commons.pool2.KeyedPooledObjectFactory;
43 import org.apache.commons.pool2.PooledObject;
44 import org.apache.commons.pool2.impl.DefaultPooledObject;
45
46 /**
47 * Implements {@link PooledConnection} that is returned by {@link DriverAdapterCPDS}.
48 *
49 * @since 2.0
50 */
51 final class PooledConnectionImpl
52 implements PooledConnection, KeyedPooledObjectFactory<PStmtKey, DelegatingPreparedStatement> {
53
54 private static final String CLOSED = "Attempted to use PooledConnection after closed() was called.";
55
56 /**
57 * The JDBC database connection that represents the physical db connection.
58 */
59 private Connection connection;
60
61 /**
62 * A DelegatingConnection used to create a PoolablePreparedStatementStub.
63 */
64 private final DelegatingConnection<?> delegatingConnection;
65
66 /**
67 * The JDBC database logical connection.
68 */
69 private Connection logicalConnection;
70
71 /**
72 * ConnectionEventListeners.
73 */
74 private final List<ConnectionEventListener> eventListeners;
75
76 /**
77 * StatementEventListeners.
78 */
79 private final List<StatementEventListener> statementEventListeners = Collections.synchronizedList(new ArrayList<>());
80
81 /**
82 * Flag set to true, once {@link #close()} is called.
83 */
84 private boolean closed;
85
86 /** My pool of {@link PreparedStatement}s. */
87 private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pStmtPool;
88
89 /**
90 * Controls access to the underlying connection.
91 */
92 private boolean accessToUnderlyingConnectionAllowed;
93
94 /**
95 * Wraps a real connection.
96 *
97 * @param connection
98 * the connection to be wrapped.
99 */
100 PooledConnectionImpl(final Connection connection) {
101 this.connection = connection;
102 if (connection instanceof DelegatingConnection) {
103 this.delegatingConnection = (DelegatingConnection<?>) connection;
104 } else {
105 this.delegatingConnection = new DelegatingConnection<>(connection);
106 }
107 eventListeners = Collections.synchronizedList(new ArrayList<>());
108 closed = false;
109 }
110
111 /**
112 * My {@link KeyedPooledObjectFactory} method for activating {@link PreparedStatement}s.
113 *
114 * @param pooledObject Activates the underlying object.
115 */
116 @Override
117 public void activateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
118 throws SQLException {
119 pooledObject.getObject().activate();
120 }
121
122 /**
123 * {@inheritDoc}
124 */
125 @Override
126 public void addConnectionEventListener(final ConnectionEventListener listener) {
127 if (!eventListeners.contains(listener)) {
128 eventListeners.add(listener);
129 }
130 }
131
132 @Override
133 public void addStatementEventListener(final StatementEventListener listener) {
134 if (!statementEventListeners.contains(listener)) {
135 statementEventListeners.add(listener);
136 }
137 }
138
139 /**
140 * Throws an SQLException, if isClosed is true
141 */
142 private void assertOpen() throws SQLException {
143 if (closed || connection == null) {
144 throw new SQLException(CLOSED);
145 }
146 }
147
148 /**
149 * Closes the physical connection and marks this {@code PooledConnection} so that it may not be used to
150 * generate any more logical {@code Connection}s.
151 *
152 * @throws SQLException
153 * Thrown when an error occurs or the connection is already closed.
154 */
155 @Override
156 public void close() throws SQLException {
157 assertOpen();
158 closed = true;
159 try {
160 if (pStmtPool != null) {
161 try {
162 pStmtPool.close();
163 } finally {
164 pStmtPool = null;
165 }
166 }
167 } catch (final RuntimeException e) {
168 throw e;
169 } catch (final Exception e) {
170 throw new SQLException("Cannot close connection (return to pool failed)", e);
171 } finally {
172 try {
173 connection.close();
174 } finally {
175 connection = null;
176 }
177 }
178 }
179
180 /**
181 * Creates a {@link PStmtKey} for the given arguments.
182 *
183 * @param sql
184 * The SQL statement.
185 * @return a {@link PStmtKey} for the given arguments.
186 */
187 protected PStmtKey createKey(final String sql) {
188 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull());
189 }
190
191 /**
192 * Creates a {@link PStmtKey} for the given arguments.
193 *
194 * @param sql
195 * The SQL statement.
196 * @param autoGeneratedKeys
197 * A flag indicating whether auto-generated keys should be returned; one of
198 * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}.
199 * @return a key to uniquely identify a prepared statement.
200 */
201 protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) {
202 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), autoGeneratedKeys);
203 }
204
205 /**
206 * Creates a {@link PStmtKey} for the given arguments.
207 *
208 * @param sql
209 * The SQL statement.
210 * @param resultSetType
211 * A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
212 * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
213 * @param resultSetConcurrency
214 * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
215 * {@code ResultSet.CONCUR_UPDATABLE}.
216 * @return a key to uniquely identify a prepared statement.
217 */
218 protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) {
219 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency);
220 }
221
222 /**
223 * Creates a {@link PStmtKey} for the given arguments.
224 *
225 * @param sql
226 * The SQL statement.
227 * @param resultSetType
228 * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
229 * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
230 * @param resultSetConcurrency
231 * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
232 * {@code ResultSet.CONCUR_UPDATABLE}
233 * @param resultSetHoldability
234 * One of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT}
235 * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}.
236 * @return a key to uniquely identify a prepared statement.
237 */
238 protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) {
239 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, resultSetHoldability);
240 }
241
242 /**
243 * Creates a {@link PStmtKey} for the given arguments.
244 *
245 * @param sql
246 * The SQL statement.
247 * @param resultSetType
248 * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
249 * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}
250 * @param resultSetConcurrency
251 * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
252 * {@code ResultSet.CONCUR_UPDATABLE}.
253 * @param resultSetHoldability
254 * One of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT}
255 * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}.
256 * @param statementType
257 * The SQL statement type, prepared or callable.
258 * @return a key to uniquely identify a prepared statement.
259 * @since 2.4.0
260 */
261 protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability,
262 final StatementType statementType) {
263 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, resultSetHoldability, statementType);
264 }
265
266 /**
267 * Creates a {@link PStmtKey} for the given arguments.
268 *
269 * @param sql
270 * The SQL statement.
271 * @param resultSetType
272 * A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
273 * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
274 * @param resultSetConcurrency
275 * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
276 * {@code ResultSet.CONCUR_UPDATABLE}.
277 * @param statementType
278 * The SQL statement type, prepared or callable.
279 * @return a key to uniquely identify a prepared statement.
280 * @since 2.4.0
281 */
282 protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, final StatementType statementType) {
283 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, statementType);
284 }
285
286 /**
287 * Creates a {@link PStmtKey} for the given arguments.
288 *
289 * @param sql
290 * The SQL statement.
291 * @param columnIndexes
292 * An array of column indexes indicating the columns that should be returned from the inserted row or
293 * rows.
294 * @return a key to uniquely identify a prepared statement.
295 */
296 protected PStmtKey createKey(final String sql, final int[] columnIndexes) {
297 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), columnIndexes);
298 }
299
300 /**
301 * Creates a {@link PStmtKey} for the given arguments.
302 *
303 * @param sql
304 * The SQL statement.
305 * @param statementType
306 * The SQL statement type, prepared or callable.
307 * @return a key to uniquely identify a prepared statement.
308 */
309 protected PStmtKey createKey(final String sql, final StatementType statementType) {
310 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), statementType);
311 }
312
313 /**
314 * Creates a {@link PStmtKey} for the given arguments.
315 *
316 * @param sql
317 * The SQL statement.
318 * @param columnNames
319 * An array of column names indicating the columns that should be returned from the inserted row or rows.
320 * @return a key to uniquely identify a prepared statement.
321 */
322 protected PStmtKey createKey(final String sql, final String[] columnNames) {
323 return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), columnNames);
324 }
325
326 /**
327 * My {@link KeyedPooledObjectFactory} method for destroying {@link PreparedStatement}s.
328 *
329 * @param key
330 * ignored
331 * @param pooledObject
332 * the wrapped {@link PreparedStatement} to be destroyed.
333 */
334 @Override
335 public void destroyObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) throws SQLException {
336 if (pooledObject != null) {
337 @SuppressWarnings("resource")
338 final DelegatingPreparedStatement object = pooledObject.getObject();
339 if (object != null) {
340 @SuppressWarnings("resource")
341 final Statement innermostDelegate = object.getInnermostDelegate();
342 if (innermostDelegate != null) {
343 innermostDelegate.close();
344 }
345 }
346 }
347 }
348
349 /**
350 * Closes the physical connection and checks that the logical connection was closed as well.
351 */
352 @Override
353 protected void finalize() throws Throwable {
354 // Closing the Connection ensures that if anyone tries to use it,
355 // an error will occur.
356 Utils.close(connection, null);
357 // make sure the last connection is marked as closed
358 if (logicalConnection != null && !logicalConnection.isClosed()) {
359 throw new SQLException("PooledConnection was gc'ed, without its last Connection being closed.");
360 }
361 }
362
363 private String getCatalogOrNull() {
364 try {
365 return connection == null ? null : connection.getCatalog();
366 } catch (final SQLException e) {
367 return null;
368 }
369 }
370
371 /**
372 * Returns a JDBC connection.
373 *
374 * @return The database connection.
375 * @throws SQLException
376 * if the connection is not open or the previous logical connection is still open
377 */
378 @Override
379 public Connection getConnection() throws SQLException {
380 assertOpen();
381 // make sure the last connection is marked as closed
382 if (logicalConnection != null && !logicalConnection.isClosed()) {
383 // should notify pool of error so the pooled connection can
384 // be removed !FIXME!
385 throw new SQLException("PooledConnection was reused, without its previous Connection being closed.");
386 }
387
388 // the spec requires that this return a new Connection instance.
389 logicalConnection = new ConnectionImpl(this, connection, isAccessToUnderlyingConnectionAllowed());
390 return logicalConnection;
391 }
392
393 private Connection getRawConnection() throws SQLException {
394 assertOpen();
395 return connection;
396 }
397
398 private String getSchemaOrNull() {
399 try {
400 return connection == null ? null : Jdbc41Bridge.getSchema(connection);
401 } catch (final SQLException e) {
402 return null;
403 }
404 }
405
406 /**
407 * Returns the value of the accessToUnderlyingConnectionAllowed property.
408 *
409 * @return true if access to the underlying is allowed, false otherwise.
410 */
411 public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
412 return this.accessToUnderlyingConnectionAllowed;
413 }
414
415 /**
416 * My {@link KeyedPooledObjectFactory} method for creating {@link PreparedStatement}s.
417 *
418 * @param key
419 * The key for the {@link PreparedStatement} to be created.
420 */
421 @SuppressWarnings("resource")
422 @Override
423 public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey key) throws SQLException {
424 if (null == key) {
425 throw new IllegalArgumentException("Prepared statement key is null or invalid.");
426 }
427 if (key.getStmtType() == StatementType.PREPARED_STATEMENT) {
428 final PreparedStatement statement = (PreparedStatement) key.createStatement(connection);
429 @SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this
430 final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pStmtPool,
431 delegatingConnection);
432 return new DefaultPooledObject<>(pps);
433 }
434 final CallableStatement statement = (CallableStatement) key.createStatement(connection);
435 @SuppressWarnings("unchecked")
436 final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pStmtPool,
437 (DelegatingConnection<Connection>) delegatingConnection);
438 return new DefaultPooledObject<>(pcs);
439 }
440
441 /**
442 * Sends a connectionClosed event.
443 */
444 void notifyListeners() {
445 final ConnectionEvent event = new ConnectionEvent(this);
446 new ArrayList<>(eventListeners).forEach(listener -> listener.connectionClosed(event));
447 }
448
449 /**
450 * My {@link KeyedPooledObjectFactory} method for passivating {@link PreparedStatement}s. Currently, invokes
451 * {@link PreparedStatement#clearParameters}.
452 *
453 * @param key
454 * ignored
455 * @param pooledObject
456 * a wrapped {@link PreparedStatement}
457 */
458 @Override
459 public void passivateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject)
460 throws SQLException {
461 @SuppressWarnings("resource")
462 final DelegatingPreparedStatement dps = pooledObject.getObject();
463 dps.clearParameters();
464 dps.passivate();
465 }
466
467 /**
468 * Creates or obtains a {@link CallableStatement} from my pool.
469 *
470 * @param sql
471 * an SQL statement that may contain one or more '?' parameter placeholders. Typically, this statement is
472 * specified using JDBC call escape syntax.
473 * @return a default {@code CallableStatement} object containing the pre-compiled SQL statement.
474 * @throws SQLException
475 * Thrown if a database access error occurs or this method is called on a closed connection.
476 * @since 2.4.0
477 */
478 @SuppressWarnings("resource") // getRawConnection() does not allocate
479 CallableStatement prepareCall(final String sql) throws SQLException {
480 if (pStmtPool == null) {
481 return getRawConnection().prepareCall(sql);
482 }
483 try {
484 return (CallableStatement) pStmtPool.borrowObject(createKey(sql, StatementType.CALLABLE_STATEMENT));
485 } catch (final RuntimeException e) {
486 throw e;
487 } catch (final Exception e) {
488 throw new SQLException("Borrow prepareCall from pool failed", e);
489 }
490 }
491
492 /**
493 * Creates or obtains a {@link CallableStatement} from my pool.
494 *
495 * @param sql
496 * a {@code String} object that is the SQL statement to be sent to the database; may contain on or
497 * more '?' parameters.
498 * @param resultSetType
499 * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
500 * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
501 * @param resultSetConcurrency
502 * a concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
503 * {@code ResultSet.CONCUR_UPDATABLE}.
504 * @return a {@code CallableStatement} object containing the pre-compiled SQL statement that will produce
505 * {@code ResultSet} objects with the given type and concurrency.
506 * @throws SQLException
507 * Thrown if a database access error occurs, this method is called on a closed connection or the given
508 * parameters are not {@code ResultSet} constants indicating type and concurrency.
509 * @since 2.4.0
510 */
511 @SuppressWarnings("resource") // getRawConnection() does not allocate
512 CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
513 throws SQLException {
514 if (pStmtPool == null) {
515 return getRawConnection().prepareCall(sql, resultSetType, resultSetConcurrency);
516 }
517 try {
518 return (CallableStatement) pStmtPool.borrowObject(
519 createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT));
520 } catch (final RuntimeException e) {
521 throw e;
522 } catch (final Exception e) {
523 throw new SQLException("Borrow prepareCall from pool failed", e);
524 }
525 }
526
527 /**
528 * Creates or obtains a {@link CallableStatement} from my pool.
529 *
530 * @param sql
531 * a {@code String} object that is the SQL statement to be sent to the database; may contain on or
532 * more '?' parameters.
533 * @param resultSetType
534 * one of the following {@code ResultSet} constants: {@code ResultSet.TYPE_FORWARD_ONLY},
535 * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
536 * @param resultSetConcurrency
537 * one of the following {@code ResultSet} constants: {@code ResultSet.CONCUR_READ_ONLY} or
538 * {@code ResultSet.CONCUR_UPDATABLE}.
539 * @param resultSetHoldability
540 * one of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT}
541 * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}.
542 * @return a new {@code CallableStatement} object, containing the pre-compiled SQL statement, that will
543 * generate {@code ResultSet} objects with the given type, concurrency, and holdability.
544 * @throws SQLException
545 * Thrown if a database access error occurs, this method is called on a closed connection or the given
546 * parameters are not {@code ResultSet} constants indicating type, concurrency, and holdability.
547 * @since 2.4.0
548 */
549 @SuppressWarnings("resource") // getRawConnection() does not allocate
550 CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
551 final int resultSetHoldability) throws SQLException {
552 if (pStmtPool == null) {
553 return getRawConnection().prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
554 }
555 try {
556 return (CallableStatement) pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency,
557 resultSetHoldability, StatementType.CALLABLE_STATEMENT));
558 } catch (final RuntimeException e) {
559 throw e;
560 } catch (final Exception e) {
561 throw new SQLException("Borrow prepareCall from pool failed", e);
562 }
563 }
564
565 /**
566 * Creates or obtains a {@link PreparedStatement} from my pool.
567 *
568 * @param sql the SQL statement.
569 * @return a {@link PoolablePreparedStatement}
570 * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or
571 * the borrow failed.
572 */
573 @SuppressWarnings("resource") // getRawConnection() does not allocate
574 PreparedStatement prepareStatement(final String sql) throws SQLException {
575 if (pStmtPool == null) {
576 return getRawConnection().prepareStatement(sql);
577 }
578 try {
579 return pStmtPool.borrowObject(createKey(sql));
580 } catch (final RuntimeException e) {
581 throw e;
582 } catch (final Exception e) {
583 throw new SQLException("Borrow prepareStatement from pool failed", e);
584 }
585 }
586
587 /**
588 * Creates or obtains a {@link PreparedStatement} from my pool.
589 *
590 * @param sql
591 * an SQL statement that may contain one or more '?' IN parameter placeholders.
592 * @param autoGeneratedKeys
593 * a flag indicating whether auto-generated keys should be returned; one of
594 * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}.
595 * @return a {@link PoolablePreparedStatement}
596 * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or
597 * the borrow failed.
598 * @see Connection#prepareStatement(String, int)
599 */
600 @SuppressWarnings("resource") // getRawConnection() does not allocate
601 PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
602 if (pStmtPool == null) {
603 return getRawConnection().prepareStatement(sql, autoGeneratedKeys);
604 }
605 try {
606 return pStmtPool.borrowObject(createKey(sql, autoGeneratedKeys));
607 } catch (final RuntimeException e) {
608 throw e;
609 } catch (final Exception e) {
610 throw new SQLException("Borrow prepareStatement from pool failed", e);
611 }
612 }
613
614 /**
615 * Creates or obtains a {@link PreparedStatement} from my pool.
616 *
617 * @param sql
618 * a {@code String} object that is the SQL statement to be sent to the database; may contain one or
619 * more '?' IN parameters.
620 * @param resultSetType
621 * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY},
622 * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}.
623 * @param resultSetConcurrency
624 * a concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or
625 * {@code ResultSet.CONCUR_UPDATABLE}.
626 *
627 * @return a {@link PoolablePreparedStatement}.
628 * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or
629 * the borrow failed.
630 * @see Connection#prepareStatement(String, int, int)
631 */
632 @SuppressWarnings("resource") // getRawConnection() does not allocate
633 PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
634 throws SQLException {
635 if (pStmtPool == null) {
636 return getRawConnection().prepareStatement(sql, resultSetType, resultSetConcurrency);
637 }
638 try {
639 return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency));
640 } catch (final RuntimeException e) {
641 throw e;
642 } catch (final Exception e) {
643 throw new SQLException("Borrow prepareStatement from pool failed", e);
644 }
645 }
646
647 @SuppressWarnings("resource") // getRawConnection() does not allocate
648 PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
649 final int resultSetHoldability) throws SQLException {
650 if (pStmtPool == null) {
651 return getRawConnection().prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
652 }
653 try {
654 return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
655 } catch (final RuntimeException e) {
656 throw e;
657 } catch (final Exception e) {
658 throw new SQLException("Borrow prepareStatement from pool failed", e);
659 }
660 }
661
662 @SuppressWarnings("resource") // getRawConnection() does not allocate
663 PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
664 if (pStmtPool == null) {
665 return getRawConnection().prepareStatement(sql, columnIndexes);
666 }
667 try {
668 return pStmtPool.borrowObject(createKey(sql, columnIndexes));
669 } catch (final RuntimeException e) {
670 throw e;
671 } catch (final Exception e) {
672 throw new SQLException("Borrow prepareStatement from pool failed", e);
673 }
674 }
675
676 @SuppressWarnings("resource") // getRawConnection() does not allocate
677 PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
678 if (pStmtPool == null) {
679 return getRawConnection().prepareStatement(sql, columnNames);
680 }
681 try {
682 return pStmtPool.borrowObject(createKey(sql, columnNames));
683 } catch (final RuntimeException e) {
684 throw e;
685 } catch (final Exception e) {
686 throw new SQLException("Borrow prepareStatement from pool failed", e);
687 }
688 }
689
690 /**
691 * {@inheritDoc}
692 */
693 @Override
694 public void removeConnectionEventListener(final ConnectionEventListener listener) {
695 eventListeners.remove(listener);
696 }
697
698 @Override
699 public void removeStatementEventListener(final StatementEventListener listener) {
700 statementEventListeners.remove(listener);
701 }
702
703 /**
704 * Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
705 * the underlying connection. (Default: false.)
706 *
707 * @param allow
708 * Access to the underlying connection is granted when true.
709 */
710 public synchronized void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
711 this.accessToUnderlyingConnectionAllowed = allow;
712 }
713
714 public void setStatementPool(final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> statementPool) {
715 pStmtPool = statementPool;
716 }
717
718 /**
719 * @since 2.6.0
720 */
721 @Override
722 public synchronized String toString() {
723 final StringBuilder builder = new StringBuilder(super.toString());
724 builder.append("[connection=");
725 builder.append(connection);
726 builder.append(", delegatingConnection=");
727 builder.append(delegatingConnection);
728 builder.append(", logicalConnection=");
729 builder.append(logicalConnection);
730 builder.append(", eventListeners=");
731 builder.append(eventListeners);
732 builder.append(", statementEventListeners=");
733 builder.append(statementEventListeners);
734 builder.append(", closed=");
735 builder.append(closed);
736 builder.append(", pStmtPool=");
737 builder.append(pStmtPool);
738 builder.append(", accessToUnderlyingConnectionAllowed=");
739 builder.append(accessToUnderlyingConnectionAllowed);
740 builder.append("]");
741 return builder.toString();
742 }
743
744 /**
745 * My {@link KeyedPooledObjectFactory} method for validating {@link PreparedStatement}s.
746 *
747 * @param key
748 * Ignored.
749 * @param pooledObject
750 * Ignored.
751 * @return {@code true}
752 */
753 @Override
754 public boolean validateObject(final PStmtKey key, final PooledObject<DelegatingPreparedStatement> pooledObject) {
755 return true;
756 }
757 }