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 {


Reply via email to