Author: wspeirs
Date: Mon May 13 19:42:50 2013
New Revision: 1482045
URL: http://svn.apache.org/r1482045
Log:
Applied patch from DBUTILS-108 and updated changes.xml
Modified:
commons/proper/dbutils/trunk/src/changes/changes.xml
commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java
commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.java
commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/QueryRunner.java
commons/proper/dbutils/trunk/src/test/java/org/apache/commons/dbutils/QueryRunnerTest.java
Modified: commons/proper/dbutils/trunk/src/changes/changes.xml
URL:
http://svn.apache.org/viewvc/commons/proper/dbutils/trunk/src/changes/changes.xml?rev=1482045&r1=1482044&r2=1482045&view=diff
==============================================================================
--- commons/proper/dbutils/trunk/src/changes/changes.xml (original)
+++ commons/proper/dbutils/trunk/src/changes/changes.xml Mon May 13 19:42:50
2013
@@ -44,19 +44,22 @@ The <action> type attribute can be add,u
</properties>
<body>
<release version="1.6" date="201?-??-??" description="Bugfixes and
addition of insert methods">
+ <action dev="wspeirs" due-to="Micah Huff" type="add" issue="DBUTILS-108">
+ Create functionality to return auto-generated keys in batches of SQL
inserts
+ </action>
<action dev="simonetripodi" due-to="Moandji Ezana" type="add"
issue="DBUTILS-98">
Add missing JavaDoc to QueryRunner#insert
</action>
<action dev="simonetripodi" type="add" issue="DBUTILS-97">
Add an Abstract ResultSetHandler implementation in order to reduce
redundant 'resultSet' variable invocation
</action>
- <action dev="wspeirs" due-to="Moandji Ezana" type="add"
issue="DBUTILS-87">
- Added insert methods to QueryRunner and AsyncQueryRunner that return
the generated key.
- </action>
<action dev="simonetripodi" due-to="yuyf" type="fix" issue="DBUTILS-96">
DbUtils#loadDriver(ClassLoader,String) makes DriverManager throwing
"No suitable driver found for jdbc"
if ClassLoader is not the System's one
</action>
+ <action dev="wspeirs" due-to="Moandji Ezana" type="add"
issue="DBUTILS-87">
+ Added insert methods to QueryRunner and AsyncQueryRunner that return
the generated key.
+ </action>
</release>
<release version="1.5" date="2012-07-20" description="Bugfixes and
addition of BeanMapHandler">
<action dev="simonetripodi" due-to="Benedikt Ritter" type="update"
issue="DBUTILS-94">
Modified:
commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java?rev=1482045&r1=1482044&r2=1482045&view=diff
==============================================================================
---
commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java
(original)
+++
commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java
Mon May 13 19:42:50 2013
@@ -153,6 +153,34 @@ public abstract class AbstractQueryRunne
return conn.prepareStatement(sql);
}
+
+ /**
+ * Factory method that creates and initializes a
+ * <code>PreparedStatement</code> object for the given SQL.
+ * <code>QueryRunner</code> methods always call this method to prepare
+ * statements for them. Subclasses can override this method to provide
+ * special PreparedStatement configuration if needed. This implementation
+ * simply calls <code>conn.prepareStatement(sql, returnedKeys)</code>
+ * which will result in the ability to retrieve the automatically-generated
+ * keys from an auto_increment column.
+ *
+ * @param conn
+ * The <code>Connection</code> used to create the
+ * <code>PreparedStatement</code>
+ * @param sql
+ * The SQL statement to prepare.
+ * @param returnedKeys
+ * Flag indicating whether to return generated keys or not.
+ *
+ * @return An initialized <code>PreparedStatement</code>.
+ * @throws SQLException
+ * if a database access error occurs
+ */
+ protected PreparedStatement prepareStatement(Connection conn, String sql,
int returnedKeys)
+ throws SQLException {
+
+ return conn.prepareStatement(sql, returnedKeys);
+ }
/**
* Factory method that creates and initializes a <code>Connection</code>
Modified:
commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.java?rev=1482045&r1=1482044&r2=1482045&view=diff
==============================================================================
---
commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.java
(original)
+++
commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.java
Mon May 13 19:42:50 2013
@@ -617,4 +617,36 @@ public class AsyncQueryRunner extends Ab
});
}
+ /**
+ * {@link QueryRunner#insertBatch(String, ResultSetHandler, Object[][])}
asynchronously.
+ *
+ * @see QueryRunner#insertBatch(String, ResultSetHandler, Object[][])
+ * @since 1.6
+ */
+ public <T> Future<T> insertBatch(final String sql, final
ResultSetHandler<T> rsh, final Object[][] params) throws SQLException {
+ return executorService.submit(new Callable<T>() {
+
+ @Override
+ public T call() throws Exception {
+ return queryRunner.insertBatch(sql, rsh, params);
+ }
+ });
+ }
+
+ /**
+ * {@link QueryRunner#insertBatch(Connection, String, ResultSetHandler,
Object[][])} asynchronously.
+ *
+ * @see QueryRunner#insertBatch(Connection, String, ResultSetHandler,
Object[][])
+ * @since 1.6
+ */
+ public <T> Future<T> insertBatch(final Connection conn, final String sql,
final ResultSetHandler<T> rsh, final Object[][] params) throws SQLException {
+ return executorService.submit(new Callable<T>() {
+
+ @Override
+ public T call() throws Exception {
+ return queryRunner.insertBatch(conn, sql, rsh, params);
+ }
+ });
+ }
+
}
Modified:
commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/QueryRunner.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/QueryRunner.java?rev=1482045&r1=1482044&r2=1482045&view=diff
==============================================================================
---
commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/QueryRunner.java
(original)
+++
commons/proper/dbutils/trunk/src/main/java/org/apache/commons/dbutils/QueryRunner.java
Mon May 13 19:42:50 2013
@@ -616,4 +616,94 @@ public class QueryRunner extends Abstrac
return generatedKeys;
}
+
+ /**
+ * Executes the given batch of INSERT SQL statements. 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 insert will not be saved.
+ * @param <T> The type of object that the handler returns
+ * @param sql The SQL statement to execute.
+ * @param rsh The handler used to create the result object from
+ * the <code>ResultSet</code> of auto-generated keys.
+ * @param params Initializes the PreparedStatement's IN (i.e. '?')
+ * @return The result generated by the handler.
+ * @throws SQLException if a database access error occurs
+ * @since 1.6
+ */
+ public <T> T insertBatch(String sql, ResultSetHandler<T> rsh, Object[][]
params) throws SQLException {
+ return insertBatch(this.prepareConnection(), true, sql, rsh, params);
+ }
+
+ /**
+ * Executes the given batch of INSERT SQL statements.
+ * @param <T> The type of object that the handler returns
+ * @param conn The connection to use to run the query.
+ * @param sql The SQL to execute.
+ * @param rsh The handler used to create the result object from
+ * the <code>ResultSet</code> of auto-generated keys.
+ * @param params The query replacement parameters.
+ * @return The result generated by the handler.
+ * @throws SQLException if a database access error occurs
+ * @since 1.6
+ */
+ public <T> T insertBatch(Connection conn, String sql, ResultSetHandler<T>
rsh, Object[][] params) throws SQLException {
+ return insertBatch(conn, false, sql, rsh, params);
+ }
+
+ /**
+ * Executes the given batch of INSERT SQL statements.
+ * @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.
+ * @param rsh The handler used to create the result object from
+ * the <code>ResultSet</code> of auto-generated keys.
+ * @param params The query replacement parameters.
+ * @return The result generated by the handler.
+ * @throws SQLException If there are database or parameter errors.
+ * @since 1.6
+ */
+ private <T> T insertBatch(Connection conn, boolean closeConn, String sql,
ResultSetHandler<T> rsh, Object[][] params) throws SQLException {
+ if (conn == null) {
+ throw new SQLException("Null connection");
+ }
+
+ if (sql == null) {
+ if (closeConn) {
+ close(conn);
+ }
+ 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;
+ T generatedKeys = null;
+ try {
+ stmt = this.prepareStatement(conn, sql,
Statement.RETURN_GENERATED_KEYS);
+
+ for (int i = 0; i < params.length; i++) {
+ this.fillStatement(stmt, params[i]);
+ stmt.addBatch();
+ }
+ stmt.executeBatch();
+ ResultSet rs = stmt.getGeneratedKeys();
+ generatedKeys = rsh.handle(rs);
+
+ } catch (SQLException e) {
+ this.rethrow(e, sql, (Object[])params);
+ } finally {
+ close(stmt);
+ if (closeConn) {
+ close(conn);
+ }
+ }
+
+ return generatedKeys;
+ }
}
Modified:
commons/proper/dbutils/trunk/src/test/java/org/apache/commons/dbutils/QueryRunnerTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/dbutils/trunk/src/test/java/org/apache/commons/dbutils/QueryRunnerTest.java?rev=1482045&r1=1482044&r2=1482045&view=diff
==============================================================================
---
commons/proper/dbutils/trunk/src/test/java/org/apache/commons/dbutils/QueryRunnerTest.java
(original)
+++
commons/proper/dbutils/trunk/src/test/java/org/apache/commons/dbutils/QueryRunnerTest.java
Mon May 13 19:42:50 2013
@@ -30,8 +30,11 @@ import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
import javax.sql.DataSource;
@@ -53,6 +56,7 @@ public class QueryRunnerTest {
@Mock PreparedStatement stmt;
@Mock ParameterMetaData meta;
@Mock ResultSet results;
+ @Mock ResultSetMetaData resultsMeta;
@Before
public void setUp() throws Exception {
@@ -416,6 +420,47 @@ public class QueryRunnerTest {
Assert.assertEquals(1L, generatedKey.longValue());
}
+
+ @Test
+ public void testGoodBatchInsert() throws Exception {
+ results = mock(ResultSet.class);
+ resultsMeta = mock(ResultSetMetaData.class);
+
+ when(meta.getParameterCount()).thenReturn(2);
+ when(conn.prepareStatement(any(String.class),
eq(Statement.RETURN_GENERATED_KEYS))).thenReturn(stmt);
+ when(stmt.getGeneratedKeys()).thenReturn(results);
+
when(results.next()).thenReturn(true).thenReturn(true).thenReturn(false);
+ when(results.getMetaData()).thenReturn(resultsMeta);
+ when(resultsMeta.getColumnCount()).thenReturn(1);
+
+ ResultSetHandler<List<Object>> handler = new
ResultSetHandler<List<Object>>()
+ {
+ public List<Object> handle(ResultSet rs) throws SQLException
+ {
+ List<Object> objects = new ArrayList<Object>();
+ while (rs.next())
+ {
+ objects.add(new Object());
+ }
+ return objects;
+ }
+ };
+
+ Object[][] params = new Object[2][2];
+ params[0][0] = "Test";
+ params[0][1] = "Blah";
+ params[1][0] = "Test2";
+ params[1][1] = "Blah2";
+
+ List<Object> generatedKeys = runner.insertBatch("INSERT INTO
blah(col1, col2) VALUES(?,?)", handler, params);
+
+ verify(stmt, times(2)).addBatch();
+ verify(stmt, times(1)).executeBatch();
+ verify(stmt, times(1)).close(); // make sure we closed the statement
+ verify(conn, times(1)).close(); // make sure we closed the
connection
+
+ Assert.assertEquals(2, generatedKeys.size());
+ }
// helper method for calling batch when an exception is expected
private void callUpdateWithException(Object... params) throws Exception {