Author: wspeirs Date: Tue Feb 26 19:30:42 2013 New Revision: 1450368 URL: http://svn.apache.org/r1450368 Log: Added BatchExecutor - Updated some of the unit tests as well
Added: commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/BatchExecutor.java commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/BatchExecutorTest.java Modified: commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AbstractExecutor.java commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/QueryRunner.java commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/QueryRunnerTest.java commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/UpdateExecutorTest.java Modified: commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AbstractExecutor.java URL: http://svn.apache.org/viewvc/commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AbstractExecutor.java?rev=1450368&r1=1450367&r2=1450368&view=diff ============================================================================== --- commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AbstractExecutor.java (original) +++ commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AbstractExecutor.java Tue Feb 26 19:30:42 2013 @@ -137,12 +137,23 @@ abstract class AbstractExecutor<T extend * @throws SQLException thrown if the parameter is not found, already bound, or there is an issue binding it. */ public T bind(String name, final Object value) throws SQLException { + return bind(name, value, true); + } + + /** + * Binds value to name, but does not do the bookkeeping. + * @param name the parameter name. + * @param value the value. + * @return this + * @throws SQLException if there is any SQLException during binding. + */ + protected T bind(String name, final Object value, boolean removeFromPosMap) throws SQLException { name = name.replace(COLON, ""); // so we can take ":name" or "name" - final List<Integer> pos = paramPosMap.remove(name); + final List<Integer> pos = removeFromPosMap ? paramPosMap.remove(name) : paramPosMap.get(name); if(pos == null) { - throw new SQLException(name + " is either not found in the SQL statement, or was already bound"); + throw new SQLException(name + " is not found in the SQL statement"); } // go through and bind all of the positions for this name @@ -160,6 +171,13 @@ abstract class AbstractExecutor<T extend return ret; } + + /** + * Used for batch calls so we can clear the map after the addBatch call. + */ + protected void clearValueMap() { + paramValueMap.clear(); + } /** * Throws a new exception with a more informative error message. Added: commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/BatchExecutor.java URL: http://svn.apache.org/viewvc/commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/BatchExecutor.java?rev=1450368&view=auto ============================================================================== --- commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/BatchExecutor.java (added) +++ commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/BatchExecutor.java Tue Feb 26 19:30:42 2013 @@ -0,0 +1,77 @@ +package org.apache.commons.dbutils; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * This class provides the ability to execute a batch of statements. + * + * It is really just a facade to an array of UpdateExecutors. + * + * @author William Speirs <wspe...@apache.org> + */ +public class BatchExecutor extends AbstractExecutor<BatchExecutor> { + + private final boolean closeConn; + + public BatchExecutor(final Connection conn, final String sql, final boolean closeConnection) throws SQLException { + super(conn, sql); + this.closeConn = closeConnection; + } + + /** + * Binds a parameter name to a value for a given statement. + * @param statement the statement number to operate on. + * @param name the name of the parameter. + * @param value the value to bind to the parameter. + * @return this object. + * @throws SQLException thrown if the statement number does not exist, or any other SQLException. + * @see org.apache.commons.dbutils.UpdateExecutor.bind(String, Object) + */ + @Override + public BatchExecutor bind(final String name, final Object value) throws SQLException { + bind(name, value, false); + + return this; + } + + /** + * Adds the statement to the batch after binding all of the parameters. + * @return this object. + * @throws SQLException if a SQLException is thrown during the addBatch() call. + * @see java.sql.PreparedStatement.addBatch() + */ + public BatchExecutor addBatch() throws SQLException { + try { + getStatement().addBatch(); + clearValueMap(); + } catch(SQLException e) { + rethrow(e); + } + + return this; + } + + /** + * Calls batch after checking the parameters to ensure nothing is null. + * @return an array containing the number of rows updated for each statement. + * @throws SQLException If there are database or parameter errors. + * @see org.apache.commons.dbutils.UpdateExecutor.update() + */ + public int[] batch() throws SQLException { + try { + return getStatement().executeBatch(); + } catch (SQLException e) { + rethrow(e); + } finally { + close(getStatement()); + if (closeConn) { + close(getConnection()); + } + } + + // we get here only if something is thrown + return null; + } + +} Modified: commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/QueryRunner.java URL: http://svn.apache.org/viewvc/commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/QueryRunner.java?rev=1450368&r1=1450367&r2=1450368&view=diff ============================================================================== --- commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/QueryRunner.java (original) +++ commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/QueryRunner.java Tue Feb 26 19:30:42 2013 @@ -48,53 +48,46 @@ public class QueryRunner extends Abstrac } /** - * Execute a batch of SQL INSERT, UPDATE, or DELETE queries. - * - * @param conn The Connection to use to run the query. The caller is - * responsible for closing this Connection. - * @param sql The SQL to execute. - * @param params An array of query replacement parameters. Each row in - * this array is one set of batch replacement values. - * @return The number of rows updated per statement. - * @throws SQLException if a database access error occurs - * @since DbUtils 1.1 + * Creates an {@link org.apache.commons.dbutils.BatchExecutor} for the given SQL. + * <code>Connection</code> is retrieved from the <code>DataSource</code> + * set in the constructor. This <code>Connection</code> must be in + * auto-commit mode or the insert will not be saved. The <code>Connection</code> is + * closed after the call. + * + * @param sql The SQL statement to execute. + * + * @return An {@link org.apache.commons.dbutils.BatchExecutor} for this SQL statement. + * @throws SQLException If there are database or parameter errors. */ - public int[] batch(Connection conn, String sql, Object[][] params) throws SQLException { - return this.batch(conn, false, sql, params); + public BatchExecutor batch(String sql) throws SQLException { + return this.batch(this.prepareConnection(), true, sql); } /** - * Execute a batch of SQL INSERT, UPDATE, or DELETE queries. The - * <code>Connection</code> is retrieved from the <code>DataSource</code> - * set in the constructor. This <code>Connection</code> must be in - * auto-commit mode or the update will not be saved. + * Creates an {@link org.apache.commons.dbutils.BatchExecutor} for the given SQL statement and connection. + * The connection is <b>NOT</b> closed after execution. * - * @param sql The SQL to execute. - * @param params An array of query replacement parameters. Each row in - * this array is one set of batch replacement values. - * @return The number of rows updated per statement. - * @throws SQLException if a database access error occurs - * @since DbUtils 1.1 + * @param conn The connection to use for the batch call. + * @param sql The SQL statement to execute. + * + * @return An {@link org.apache.commons.dbutils.BatchExecutor} for this SQL statement. + * @throws SQLException If there are database or parameter errors. */ - public int[] batch(String sql, Object[][] params) throws SQLException { - Connection conn = this.prepareConnection(); - - return this.batch(conn, true, sql, params); + public BatchExecutor batch(Connection conn, String sql) throws SQLException { + return this.batch(conn, true, sql); } /** - * Calls update after checking the parameters to ensure nothing is null. + * Creates an {@link org.apache.commons.dbutils.BatchExecutor} for the given SQL statement and connection. + * * @param conn The connection to use for the batch call. * @param closeConn True if the connection should be closed, false otherwise. * @param sql The SQL statement to execute. - * @param params An array of query replacement parameters. Each row in - * this array is one set of batch replacement values. - * @return The number of rows updated in the batch. + * + * @return An {@link org.apache.commons.dbutils.BatchExecutor} for this SQL statement. * @throws SQLException If there are database or parameter errors. */ - private int[] batch(Connection conn, boolean closeConn, String sql, Object[][] params) throws SQLException { - throw new SQLException("Not yet implemented"); -/* + public BatchExecutor batch(Connection conn, boolean closeConn, String sql) throws SQLException { if (conn == null) { throw new SQLException("Null connection"); } @@ -105,71 +98,47 @@ public class QueryRunner extends Abstrac } throw new SQLException("Null SQL statement"); } - - if (params == null) { - if (closeConn) { - close(conn); - } - throw new SQLException("Null parameters. If parameters aren't need, pass an empty array."); - } - - PreparedStatement stmt = null; - int[] rows = null; - try { - stmt = this.prepareStatement(conn, sql); - - for (int i = 0; i < params.length; i++) { - this.fillStatement(stmt, params[i]); - stmt.addBatch(); - } - rows = stmt.executeBatch(); - - } catch (SQLException e) { - this.rethrow(e, sql, (Object[])params); - } finally { - close(stmt); - if (closeConn) { - close(conn); - } - } - - return rows; -*/ + + return new BatchExecutor(conn, sql, closeConn); } /** - * Creates an {@link org.apache.commons.dbutils.QueryExecutor} for the given SQL statement. - * The <code>Connection</code> is retrieved from the - * <code>DataSource</code> set in the constructor. + * Creates an {@link org.apache.commons.dbutils.QueryExecutor} for the given SQL. + * <code>Connection</code> is retrieved from the <code>DataSource</code> + * set in the constructor. This <code>Connection</code> must be in + * auto-commit mode or the insert will not be saved. The <code>Connection</code> is + * closed after the call. + * * @param sql The SQL statement to execute. + * * @return An {@link org.apache.commons.dbutils.QueryExecutor} for this SQL statement. * @throws SQLException If there are database or parameter errors. */ public QueryExecutor query(String sql) throws SQLException { - Connection conn = this.prepareConnection(); - - return this.query(conn, true, sql); + return this.query(this.prepareConnection(), true, sql); } /** * Creates an {@link org.apache.commons.dbutils.QueryExecutor} for the given SQL statement and connection. * The connection is <b>NOT</b> closed after execution. + * * @param conn The connection to use for the update call. * @param sql The SQL statement to execute. + * * @return An {@link org.apache.commons.dbutils.QueryExecutor} for this SQL statement. * @throws SQLException If there are database or parameter errors. */ - public QueryExecutor query(Connection con, String sql) throws SQLException { - final Connection conn = this.prepareConnection(); - - return this.query(conn, true, sql); + public QueryExecutor query(Connection conn, String sql) throws SQLException { + return this.query(conn, false, sql); } /** * Creates an {@link org.apache.commons.dbutils.QueryExecutor} for the given SQL statement and connection. - * @param conn The connection to use for the update call. + * + * @param conn The connection to use for the query call. * @param closeConn True if the connection should be closed, false otherwise. * @param sql The SQL statement to execute. + * * @return An {@link org.apache.commons.dbutils.QueryExecutor} for this SQL statement. * @throws SQLException If there are database or parameter errors. */ @@ -189,26 +158,28 @@ public class QueryRunner extends Abstrac } /** - * Creates an {@link org.apache.commons.dbutils.UpdateExecutor} for the given SQL statement. + * Creates an {@link org.apache.commons.dbutils.UpdateExecutor} for the given SQL. * <code>Connection</code> is retrieved from the <code>DataSource</code> * set in the constructor. This <code>Connection</code> must be in - * auto-commit mode or the update will not be saved. + * auto-commit mode or the insert will not be saved. The <code>Connection</code> is + * closed after the call. * * @param sql The SQL statement to execute. + * * @return An {@link org.apache.commons.dbutils.UpdateExecutor} for this SQL statement. * @throws SQLException if a database access error occurs */ public UpdateExecutor update(String sql) throws SQLException { - Connection conn = this.prepareConnection(); - - return this.update(conn, true, sql); + return this.update(this.prepareConnection(), true, sql); } /** * Creates an {@link org.apache.commons.dbutils.UpdateExecutor} for the given SQL statement and connection. * The connection is <b>NOT</b> closed after execution. + * * @param conn The connection to use for the update call. * @param sql The SQL statement to execute. + * * @return An {@link org.apache.commons.dbutils.UpdateExecutor} for this SQL statement. * @throws SQLException If there are database or parameter errors. */ @@ -218,9 +189,11 @@ public class QueryRunner extends Abstrac /** * Creates an {@link org.apache.commons.dbutils.UpdateExecutor} for the given SQL statement and connection. + * * @param conn The connection to use for the update call. * @param closeConn True if the connection should be closed, false otherwise. * @param sql The SQL statement to execute. + * * @return An {@link org.apache.commons.dbutils.UpdateExecutor} for this SQL statement. * @throws SQLException If there are database or parameter errors. */ @@ -243,8 +216,9 @@ public class QueryRunner extends Abstrac * Creates an {@link org.apache.commons.dbutils.InsertExecutor} for the given SQL. * <code>Connection</code> is retrieved from the <code>DataSource</code> * set in the constructor. This <code>Connection</code> must be in - * auto-commit mode or the insert will not be saved. - * @param conn The connection to use for the query call. + * auto-commit mode or the insert will not be saved. The <code>Connection</code> is + * closed after the call. + * * @param sql The SQL statement to execute. * * @return An {@link org.apache.commons.dbutils.InsertExecutor} for this SQL statement. @@ -257,6 +231,7 @@ public class QueryRunner extends Abstrac /** * Creates an {@link org.apache.commons.dbutils.InsertExecutor} for the given SQL and connection * The connection is <b>NOT</b> closed after execution. + * * @param conn The connection to use for the query call. * @param sql The SQL statement to execute. * @@ -269,7 +244,8 @@ public class QueryRunner extends Abstrac /** * Creates an {@link org.apache.commons.dbutils.InsertExecutor} for the given SQL and connection. - * @param conn The connection to use for the query call. + * + * @param conn The connection to use for the insert call. * @param closeConn True if the connection should be closed, false otherwise. * @param sql The SQL statement to execute. * Added: commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/BatchExecutorTest.java URL: http://svn.apache.org/viewvc/commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/BatchExecutorTest.java?rev=1450368&view=auto ============================================================================== --- commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/BatchExecutorTest.java (added) +++ commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/BatchExecutorTest.java Tue Feb 26 19:30:42 2013 @@ -0,0 +1,67 @@ +/* + * 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.dbutils; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + + +public class BatchExecutorTest { + + private BatchExecutor executor; + + @Mock private Connection conn; + @Mock private PreparedStatement stmt; + + @Before + public void setup() throws SQLException { + MockitoAnnotations.initMocks(this); + + when(conn.prepareStatement(any(String.class))).thenReturn(stmt); + when(stmt.executeBatch()).thenReturn(new int[] { 2, 3, 4 }); + } + + protected void createExecutor(String sql) throws Exception { + executor = new BatchExecutor(conn, sql, true); + } + + @Test + public void testGoodSQL() throws Exception { + createExecutor("insert into blah"); + + executor.addBatch(); + int[] ret = executor.batch(); + + assertEquals(3, ret.length); + assertEquals(2, ret[0]); + assertEquals(3, ret[1]); + assertEquals(4, ret[2]); + verify(conn, times(1)).close(); + verify(stmt, times(1)).close(); + } + +} Modified: commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/QueryRunnerTest.java URL: http://svn.apache.org/viewvc/commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/QueryRunnerTest.java?rev=1450368&r1=1450367&r2=1450368&view=diff ============================================================================== --- commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/QueryRunnerTest.java (original) +++ commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/QueryRunnerTest.java Tue Feb 26 19:30:42 2013 @@ -43,6 +43,34 @@ public class QueryRunnerTest { runner = new QueryRunner(dataSource); } + // batch tests + + @Test + public void testBatchSQL() throws SQLException { + assertNotNull(runner.batch("select * from blah where :first=first")); + verify(dataSource, times(1)).getConnection(); + } + + @Test + public void testBatchConnSQL() throws SQLException { + assertNotNull(runner.batch(conn, "select * from blah where :first=first")); + } + + @Test + public void testBatchConnSQLBoolean() throws SQLException { + assertNotNull(runner.batch(conn, true, "select * from blah where :first=first")); + } + + @Test(expected=SQLException.class) + public void testBatchNullConn() throws SQLException { + assertNotNull(runner.batch(null, true, "select")); + } + + @Test(expected=SQLException.class) + public void testBatchNullSQL() throws SQLException { + assertNotNull(runner.batch(conn, true, null)); + } + // query tests @Test @@ -61,6 +89,16 @@ public class QueryRunnerTest { assertNotNull(runner.query(conn, true, "select * from blah where :first=first")); } + @Test(expected=SQLException.class) + public void testQueryNullConn() throws SQLException { + assertNotNull(runner.query(null, true, "select")); + } + + @Test(expected=SQLException.class) + public void testQueryNullSQL() throws SQLException { + assertNotNull(runner.query(conn, true, null)); + } + // insert tests @Test @@ -79,6 +117,16 @@ public class QueryRunnerTest { assertNotNull(runner.insert(conn, true, "insert * from blah where :first=first")); } + @Test(expected=SQLException.class) + public void testInsertNullConn() throws SQLException { + assertNotNull(runner.insert(null, true, "select")); + } + + @Test(expected=SQLException.class) + public void testInsertNullSQL() throws SQLException { + assertNotNull(runner.insert(conn, true, null)); + } + // update tests @Test @@ -96,5 +144,16 @@ public class QueryRunnerTest { public void testUpdateConnSQLBoolean() throws SQLException { assertNotNull(runner.update(conn, true, "select * from blah where :first=first")); } + + @Test(expected=SQLException.class) + public void testUpdateNullConn() throws SQLException { + assertNotNull(runner.update(null, true, "select")); + } + + @Test(expected=SQLException.class) + public void testUpdateNullSQL() throws SQLException { + assertNotNull(runner.update(conn, true, null)); + } + } Modified: commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/UpdateExecutorTest.java URL: http://svn.apache.org/viewvc/commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/UpdateExecutorTest.java?rev=1450368&r1=1450367&r2=1450368&view=diff ============================================================================== --- commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/UpdateExecutorTest.java (original) +++ commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/UpdateExecutorTest.java Tue Feb 26 19:30:42 2013 @@ -16,14 +16,13 @@ */ package org.apache.commons.dbutils; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.sql.Connection; import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; import org.junit.Before; import org.junit.Test; @@ -37,14 +36,13 @@ public class UpdateExecutorTest { @Mock private Connection conn; @Mock private PreparedStatement stmt; - @Mock private ResultSet resultSet; @Before public void setup() throws SQLException { MockitoAnnotations.initMocks(this); when(conn.prepareStatement(any(String.class))).thenReturn(stmt); - when(stmt.executeQuery()).thenReturn(resultSet); + when(stmt.executeUpdate()).thenReturn(20); } protected void createExecutor(String sql) throws Exception { @@ -55,9 +53,9 @@ public class UpdateExecutorTest { public void testGoodSQL() throws Exception { createExecutor("insert into blah"); - Object ret = executor.update(); + int ret = executor.update(); - assertNotNull(ret); + assertEquals(20, ret); verify(conn, times(1)).close(); verify(stmt, times(1)).close(); } @@ -66,9 +64,9 @@ public class UpdateExecutorTest { public void testUnmappedParams() throws Exception { createExecutor("insert into blah (:something)"); - Object ret = executor.update(); + int ret = executor.update(); - assertNotNull(ret); + assertEquals(20, ret); verify(conn, times(1)).close(); verify(stmt, times(1)).close(); }