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.PreparedStatement; 020import java.sql.SQLException; 021 022import org.apache.commons.pool2.KeyedObjectPool; 023 024/** 025 * A {@link DelegatingPreparedStatement} that cooperates with {@link PoolingConnection} to implement a pool of 026 * {@link PreparedStatement}s. 027 * <p> 028 * My {@link #close} method returns me to my containing pool. (See {@link PoolingConnection}.) 029 * </p> 030 * 031 * @param <K> 032 * the key type 033 * 034 * @see PoolingConnection 035 * @since 2.0 036 */ 037public class PoolablePreparedStatement<K> extends DelegatingPreparedStatement { 038 039 /** 040 * The {@link KeyedObjectPool} from which I was obtained. 041 */ 042 private final KeyedObjectPool<K, PoolablePreparedStatement<K>> pool; 043 044 /** 045 * My "key" as used by {@link KeyedObjectPool}. 046 */ 047 private final K key; 048 049 private volatile boolean batchAdded; 050 051 /** 052 * Constructs a new instance. 053 * 054 * @param stmt 055 * my underlying {@link PreparedStatement} 056 * @param key 057 * my key as used by {@link KeyedObjectPool} 058 * @param pool 059 * the {@link KeyedObjectPool} from which I was obtained. 060 * @param conn 061 * the {@link java.sql.Connection Connection} from which I was created 062 */ 063 public PoolablePreparedStatement(final PreparedStatement stmt, final K key, 064 final KeyedObjectPool<K, PoolablePreparedStatement<K>> pool, final DelegatingConnection<?> conn) { 065 super(conn, stmt); 066 this.pool = pool; 067 this.key = key; 068 069 // Remove from trace now because this statement will be 070 // added by the activate method. 071 removeThisTrace(conn); 072 } 073 074 @Override 075 public void activate() throws SQLException { 076 setClosedInternal(false); 077 AbandonedTrace.add(getConnectionInternal(), this); 078 super.activate(); 079 } 080 081 /** 082 * Add batch. 083 */ 084 @Override 085 public void addBatch() throws SQLException { 086 super.addBatch(); 087 batchAdded = true; 088 } 089 090 /** 091 * Clear Batch. 092 */ 093 @Override 094 public void clearBatch() throws SQLException { 095 batchAdded = false; 096 super.clearBatch(); 097 } 098 099 /** 100 * Return me to my pool. 101 */ 102 @Override 103 public void close() throws SQLException { 104 // calling close twice should have no effect 105 if (!isClosed()) { 106 try { 107 pool.returnObject(key, this); 108 } catch (final SQLException | RuntimeException e) { 109 throw e; 110 } catch (final Exception e) { 111 throw new SQLException("Cannot close preparedstatement (return to pool failed)", e); 112 } 113 } 114 } 115 116 /** 117 * Package-protected for tests. 118 * 119 * @return The key. 120 */ 121 K getKey() { 122 return key; 123 } 124 125 @Override 126 public void passivate() throws SQLException { 127 // DBCP-372. clearBatch with throw an exception if called when the 128 // connection is marked as closed. 129 if (batchAdded) { 130 clearBatch(); 131 } 132 prepareToReturn(); 133 } 134}