Repository: commons-dbcp Updated Branches: refs/heads/master 8a1c11185 -> e6543480a
Fix DBCP-462 Refactoring to prepare for a future patch to enable pooling of all prepared and callable statements in PoolingConnection. Project: http://git-wip-us.apache.org/repos/asf/commons-dbcp/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-dbcp/commit/e6543480 Tree: http://git-wip-us.apache.org/repos/asf/commons-dbcp/tree/e6543480 Diff: http://git-wip-us.apache.org/repos/asf/commons-dbcp/diff/e6543480 Branch: refs/heads/master Commit: e6543480ad73e45c7f02710b0ee15352664e118a Parents: 8a1c111 Author: Mark Thomas <[email protected]> Authored: Thu Nov 3 08:53:42 2016 +0000 Committer: Mark Thomas <[email protected]> Committed: Thu Nov 3 08:53:42 2016 +0000 ---------------------------------------------------------------------- src/changes/changes.xml | 4 + .../java/org/apache/commons/dbcp2/PStmtKey.java | 108 ++++++++++++++++++- .../apache/commons/dbcp2/PoolingConnection.java | 32 ++---- .../commons/dbcp2/TestPoolingConnection.java | 106 ++++++++++++++++++ .../apache/commons/dbcp2/TesterConnection.java | 7 +- .../commons/dbcp2/TesterPreparedStatement.java | 20 ++++ 6 files changed, 247 insertions(+), 30 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/e6543480/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index a3e5daa..da71653 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -109,6 +109,10 @@ The <action> type attribute can be add,update,fix,remove. remains but is deprecated so not to break clients currently using the incorrect name. </action> + <action dev="markt" type="add" issue="DBCP-462" due-to=" Keiichi Fujino"> + Refactoring to prepare for a future patch to enable pooling of all + prepared and callable statements in PoolingConnection. + </action> </release> <release version="2.1.1" date="6 Aug 2015" description= "This is a patch release, including bug fixes only."> http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/e6543480/src/main/java/org/apache/commons/dbcp2/PStmtKey.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/dbcp2/PStmtKey.java b/src/main/java/org/apache/commons/dbcp2/PStmtKey.java index 95a1d89..4d5dc90 100644 --- a/src/main/java/org/apache/commons/dbcp2/PStmtKey.java +++ b/src/main/java/org/apache/commons/dbcp2/PStmtKey.java @@ -16,6 +16,11 @@ */ package org.apache.commons.dbcp2; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; + import org.apache.commons.dbcp2.PoolingConnection.StatementType; /** @@ -43,13 +48,30 @@ public class PStmtKey { /** Statement type */ private final StatementType _stmtType; + /** Statement builder */ + private StatementeBuilder builder; public PStmtKey(final String sql) { - this(sql, null, StatementType.PREPARED_STATEMENT, null); + this(sql, null, StatementType.PREPARED_STATEMENT); } public PStmtKey(final String sql, final String catalog) { - this(sql, catalog, StatementType.PREPARED_STATEMENT, null); + this(sql, catalog, StatementType.PREPARED_STATEMENT); + } + + public PStmtKey(final String sql, final String catalog, final StatementType stmtType) { + _sql = sql; + _catalog = catalog; + _stmtType = stmtType; + _autoGeneratedKeys = null; + _resultSetType = null; + _resultSetConcurrency = null; + // create builder + if (stmtType == StatementType.PREPARED_STATEMENT) { + builder = new PreparedStatementSQL(); + } else if (stmtType == StatementType.CALLABLE_STATEMENT) { + builder = new PreparedCallSQL(); + } } public PStmtKey(final String sql, final String catalog, final int autoGeneratedKeys) { @@ -63,6 +85,12 @@ public class PStmtKey { _autoGeneratedKeys = autoGeneratedKeys; _resultSetType = null; _resultSetConcurrency = null; + // create builder + if (stmtType == StatementType.PREPARED_STATEMENT) { + builder = new PreparedStatementWithAutoGeneratedKeys(); + } else if (stmtType == StatementType.CALLABLE_STATEMENT) { + builder = new PreparedCallSQL(); + } } public PStmtKey(final String sql, final int resultSetType, final int resultSetConcurrency) { @@ -80,6 +108,12 @@ public class PStmtKey { _resultSetConcurrency = Integer.valueOf(resultSetConcurrency); _stmtType = stmtType; _autoGeneratedKeys = null; + // create builder + if (stmtType == StatementType.PREPARED_STATEMENT) { + builder = new PreparedStatementWithResultSetConcurrency(); + } else if (stmtType == StatementType.CALLABLE_STATEMENT) { + builder = new PreparedCallWithResultSetConcurrency(); + } } @@ -190,4 +224,74 @@ public class PStmtKey { buf.append(_stmtType); return buf.toString(); } + + public Statement createStatement(Connection connection) throws SQLException { + if (builder == null) throw new IllegalStateException("Prepared statement key is invalid."); + return builder.createStatement(connection); + } + + /** + * Interface for Prepared or Callable Statement + */ + private interface StatementeBuilder { + public Statement createStatement(Connection connection) throws SQLException; + } + + /** + * Builder for prepareStatement(String sql) + */ + private class PreparedStatementSQL implements StatementeBuilder { + @Override + public Statement createStatement(Connection connection) throws SQLException { + PreparedStatement statement = connection.prepareStatement(_sql); + return statement; + } + } + + /** + * Builder for prepareStatement(String sql, int autoGeneratedKeys) + */ + private class PreparedStatementWithAutoGeneratedKeys implements StatementeBuilder { + @Override + public Statement createStatement(Connection connection) throws SQLException { + PreparedStatement statement = connection.prepareStatement( + _sql, _autoGeneratedKeys.intValue()); + return statement; + } + } + + /** + * Builder for prepareStatement(String sql, int resultSetType, int resultSetConcurrency) + */ + private class PreparedStatementWithResultSetConcurrency implements StatementeBuilder { + @Override + public Statement createStatement(Connection connection) throws SQLException { + PreparedStatement statement = connection.prepareStatement( + _sql, _resultSetType.intValue(), _resultSetConcurrency.intValue()); + return statement; + } + } + + /** + * Builder for prepareCall(String sql) + */ + private class PreparedCallSQL implements StatementeBuilder { + @Override + public Statement createStatement(Connection connection) throws SQLException { + PreparedStatement statement = connection.prepareCall(_sql); + return statement; + } + } + + /** + * Builder for prepareCall(String sql, int resultSetType, int resultSetConcurrency) + */ + private class PreparedCallWithResultSetConcurrency implements StatementeBuilder { + @Override + public Statement createStatement(Connection connection) throws SQLException { + PreparedStatement statement = connection.prepareCall( + _sql, _resultSetType.intValue(), _resultSetConcurrency.intValue()); + return statement; + } + } } http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/e6543480/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java b/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java index 2f1eab4..1fd9bbe 100644 --- a/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java +++ b/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java @@ -317,33 +317,15 @@ public class PoolingConnection extends DelegatingConnection<Connection> if(null == key) { throw new IllegalArgumentException("Prepared statement key is null or invalid."); } - if (null == key.getResultSetType() && null == key.getResultSetConcurrency() && null == key.getAutoGeneratedKeys()) { - if (key.getStmtType() == StatementType.PREPARED_STATEMENT ) { - @SuppressWarnings({"rawtypes", "unchecked"}) // Unable to find way to avoid this - final - PoolablePreparedStatement pps = new PoolablePreparedStatement( - getDelegate().prepareStatement(key.getSql()), key, _pstmtPool, this); - return new DefaultPooledObject<DelegatingPreparedStatement>(pps); - } - return new DefaultPooledObject<DelegatingPreparedStatement>( - new PoolableCallableStatement(getDelegate().prepareCall( key.getSql()), key, _pstmtPool, this)); - } else if (null == key.getResultSetType() && null == key.getResultSetConcurrency()){ + if (key.getStmtType() == StatementType.PREPARED_STATEMENT ) { + PreparedStatement statement = (PreparedStatement) key.createStatement(getDelegate()); @SuppressWarnings({"rawtypes", "unchecked"}) // Unable to find way to avoid this - final - PoolablePreparedStatement pps = new PoolablePreparedStatement( - getDelegate().prepareStatement(key.getSql(), key.getAutoGeneratedKeys().intValue()), key, _pstmtPool, this); + PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, _pstmtPool, this); return new DefaultPooledObject<DelegatingPreparedStatement>(pps); - } else { // Both _resultSetType and _resultSetConcurrency are non-null here (both or neither are set by constructors) - if(key.getStmtType() == StatementType.PREPARED_STATEMENT) { - @SuppressWarnings({"rawtypes", "unchecked"}) // Unable to find way to avoid this - final - PoolablePreparedStatement pps = new PoolablePreparedStatement(getDelegate().prepareStatement( - key.getSql(), key.getResultSetType().intValue(),key.getResultSetConcurrency().intValue()), key, _pstmtPool, this); - return new DefaultPooledObject<DelegatingPreparedStatement>(pps); - } - return new DefaultPooledObject<DelegatingPreparedStatement>( - new PoolableCallableStatement( getDelegate().prepareCall( - key.getSql(),key.getResultSetType().intValue(), key.getResultSetConcurrency().intValue()), key, _pstmtPool, this)); + } else { + CallableStatement statement = (CallableStatement) key.createStatement(getDelegate()); + PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, _pstmtPool, this); + return new DefaultPooledObject<DelegatingPreparedStatement>(pcs); } } http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/e6543480/src/test/java/org/apache/commons/dbcp2/TestPoolingConnection.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/dbcp2/TestPoolingConnection.java b/src/test/java/org/apache/commons/dbcp2/TestPoolingConnection.java new file mode 100644 index 0000000..101fa6f --- /dev/null +++ b/src/test/java/org/apache/commons/dbcp2/TestPoolingConnection.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.dbcp2; + +import static org.junit.Assert.assertEquals; + +import org.apache.commons.pool2.KeyedObjectPool; +import org.apache.commons.pool2.impl.GenericKeyedObjectPool; +import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class TestPoolingConnection { + + private PoolingConnection con = null; + + @Before + public void setUp() throws Exception { + con = new PoolingConnection(new TesterConnection("test", "test")); + final GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig(); + config.setMaxTotalPerKey(-1); + config.setBlockWhenExhausted(false); + config.setMaxWaitMillis(0); + config.setMaxIdlePerKey(1); + config.setMaxTotal(1); + final KeyedObjectPool<PStmtKey,DelegatingPreparedStatement> stmtPool = + new GenericKeyedObjectPool<>(con, config); + con.setStatementPool(stmtPool); + } + + @After + public void tearDown() throws Exception { + con.close(); + con = null; + } + + @Test + public void testPrepareStatement() throws Exception { + String sql = "select 'a' from dual"; + DelegatingPreparedStatement statement = (DelegatingPreparedStatement)con.prepareStatement(sql); + TesterPreparedStatement testStatement = (TesterPreparedStatement) statement.getInnermostDelegate(); + // assert + assertEquals(sql, testStatement.getSql()); + } + + @Test + public void testPrepareStatementWithAutoGeneratedKeys() throws Exception { + String sql = "select 'a' from dual"; + int autoGeneratedKeys = 0; + DelegatingPreparedStatement statement = (DelegatingPreparedStatement)con.prepareStatement(sql, autoGeneratedKeys); + TesterPreparedStatement testStatement = (TesterPreparedStatement) statement.getInnermostDelegate(); + // assert + assertEquals(sql, testStatement.getSql()); + assertEquals(autoGeneratedKeys, testStatement.getAutoGeneratedKeys()); + } + + @Test + public void testPrepareStatementWithResultSetConcurrency() throws Exception { + String sql = "select 'a' from dual"; + int resultSetType = 0; + int resultSetConcurrency = 0; + DelegatingPreparedStatement statement = (DelegatingPreparedStatement)con.prepareStatement(sql, resultSetType, resultSetConcurrency); + TesterPreparedStatement testStatement = (TesterPreparedStatement) statement.getInnermostDelegate(); + // assert + assertEquals(sql, testStatement.getSql()); + assertEquals(resultSetType, testStatement.getResultSetType()); + assertEquals(resultSetConcurrency, testStatement.getResultSetConcurrency()); + } + + @Test + public void testPrepareCall() throws Exception { + String sql = "select 'a' from dual"; + DelegatingCallableStatement statement = (DelegatingCallableStatement)con.prepareCall(sql); + TesterCallableStatement testStatement = (TesterCallableStatement) statement.getInnermostDelegate(); + // assert + assertEquals(sql, testStatement.getSql()); + } + + @Test + public void testPrepareCallWithResultSetConcurrency() throws Exception { + String sql = "select 'a' from dual"; + int resultSetType = 0; + int resultSetConcurrency = 0; + DelegatingCallableStatement statement = (DelegatingCallableStatement)con.prepareCall(sql, resultSetType, resultSetConcurrency); + TesterCallableStatement testStatement = (TesterCallableStatement) statement.getInnermostDelegate(); + // assert + assertEquals(sql, testStatement.getSql()); + assertEquals(resultSetType, testStatement.getResultSetType()); + assertEquals(resultSetConcurrency, testStatement.getResultSetConcurrency()); + } +} http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/e6543480/src/test/java/org/apache/commons/dbcp2/TesterConnection.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/dbcp2/TesterConnection.java b/src/test/java/org/apache/commons/dbcp2/TesterConnection.java index ab0f665..3be2a45 100644 --- a/src/test/java/org/apache/commons/dbcp2/TesterConnection.java +++ b/src/test/java/org/apache/commons/dbcp2/TesterConnection.java @@ -159,13 +159,13 @@ public class TesterConnection implements Connection { if ("warning".equals(sql)) { setWarnings(new SQLWarning("warning in prepareCall")); } - return new TesterCallableStatement(this); + return new TesterCallableStatement(this, sql); } @Override public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency) throws SQLException { checkOpen(); - return new TesterCallableStatement(this); + return new TesterCallableStatement(this, sql, resultSetType, resultSetConcurrency); } @Override @@ -302,7 +302,8 @@ public class TesterConnection implements Connection { @Override public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException { - return prepareStatement(sql); + checkOpen(); + return new TesterPreparedStatement(this, sql, autoGeneratedKeys); } @Override http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/e6543480/src/test/java/org/apache/commons/dbcp2/TesterPreparedStatement.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/dbcp2/TesterPreparedStatement.java b/src/test/java/org/apache/commons/dbcp2/TesterPreparedStatement.java index 292f9c1..147a9dc 100644 --- a/src/test/java/org/apache/commons/dbcp2/TesterPreparedStatement.java +++ b/src/test/java/org/apache/commons/dbcp2/TesterPreparedStatement.java @@ -45,6 +45,7 @@ public class TesterPreparedStatement extends TesterStatement implements Prepared private final ResultSetMetaData _resultSetMetaData = null; private String _sql = null; private String _catalog = null; + private int _autoGeneratedKeys = 1; public TesterPreparedStatement(final Connection conn) { super(conn); @@ -65,6 +66,17 @@ public class TesterPreparedStatement extends TesterStatement implements Prepared } } + public TesterPreparedStatement(final Connection conn, final String sql, final int autoGeneratedKeys) { + super(conn); + _sql = sql; + _autoGeneratedKeys = autoGeneratedKeys; + try { + _catalog = conn.getCatalog(); + } catch (final SQLException e) { + // Ignored + } + } + public TesterPreparedStatement(final Connection conn, final String sql, final int resultSetType, final int resultSetConcurrency) { super(conn, resultSetType, resultSetConcurrency); _sql = sql; @@ -76,10 +88,18 @@ public class TesterPreparedStatement extends TesterStatement implements Prepared } /** for junit test only */ + public String getSql() { + return _sql; + } + public String getCatalog() { return _catalog; } + public int getAutoGeneratedKeys() { + return _autoGeneratedKeys; + } + @Override public ResultSet executeQuery(final String sql) throws SQLException { checkOpen();
