You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ws...@apache.org on 2013/02/26 04:52:38 UTC

svn commit: r1450009 [1/2] - in /commons/proper/dbutils/branches/2_0/src: main/java/org/apache/commons/dbutils/ test/java/org/apache/commons/dbutils/

Author: wspeirs
Date: Tue Feb 26 03:52:37 2013
New Revision: 1450009

URL: http://svn.apache.org/r1450009
Log:
Major re-write of QueryRunner
- QueryRunner now returns Executors which provide a fluent API for binding parameters
- Parameters are bound by name instead of position
- AsyncQueryRunner runs an Executor
- Unit tests updated to match changes
**Update is NOT included yet, still working on the design of the Executors

Added:
    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/InsertExecutor.java
    commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/QueryExecutor.java
    commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/UpdateExecutor.java
    commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/AbstractExecutorTest.java
    commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/InsertExecutorTest.java
    commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/QueryExecutorTest.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/AbstractQueryRunner.java
    commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.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/AsyncQueryRunnerTest.java
    commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/QueryRunnerTest.java

Added: 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=1450009&view=auto
==============================================================================
--- commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AbstractExecutor.java (added)
+++ commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AbstractExecutor.java Tue Feb 26 03:52:37 2013
@@ -0,0 +1,249 @@
+/*
+ * 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 java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Abstract class for executing a query, insert, update, or batch.
+ * 
+ * @since 2.0
+ * @author William Speirs <ws...@apache.org>
+ */
+abstract class AbstractExecutor<T extends AbstractExecutor<T>> {
+    
+    private static final String COLON = ":";  // TODO: change this to any character
+
+    private final Connection conn;
+    private final String sql;
+    private final PreparedStatement stmt;
+
+    private final Map<String, List<Integer>> paramPosMap;
+    private final Map<String, Object> paramValueMap;
+    private Integer currentPosition = Integer.valueOf(0);
+    
+    public AbstractExecutor(final Connection conn, final String sql) throws SQLException {
+        this.conn = conn;
+        this.sql = sql;
+        this.paramPosMap = new HashMap<String, List<Integer>>();
+        this.paramValueMap = new HashMap<String, Object>();
+        
+        final Pattern paramPattern = Pattern.compile("(:\\w+)");
+        final Matcher matcher = paramPattern.matcher(sql);
+
+        // go through finding params
+        while(matcher.find()) {
+            insertParamPosition(matcher.group().replace(COLON, ""));
+        }
+        
+        // replace all of the :names with ?, and create a prepared statement
+        stmt = conn.prepareStatement(sql.replaceAll(":\\w+", "\\?"));
+    }
+    
+    /**
+     * Helper method to insert params and the current position into the map.
+     * @param param the SQL param.
+     */
+    private void insertParamPosition(final String param) {
+        List<Integer> posList = paramPosMap.get(param);
+        
+        // create a new list if we need to
+        if(posList == null) {
+            posList = new ArrayList<Integer>();
+            paramPosMap.put(param, posList);
+        }
+        
+        // increment first, so we match SQL numbering
+        posList.add(++currentPosition);
+    }
+    
+    /**
+     * Gets the SQL statement that was passed into the constructor.
+     * @return the SQL statement passed into the constructor.
+     */
+    protected String getSql() {
+        return sql;
+    }
+    
+    /**
+     * Returns the underlying prepared statement.
+     * @return the underlying prepared statement.
+     */
+    protected PreparedStatement getStatement() {
+        return stmt;
+    }
+    
+    /**
+     * Returns the underlying connection.
+     * @return the underlying connection.
+     */
+    protected Connection getConnection() {
+        return conn;
+    }
+    
+    /**
+     * Throws an exception if there are unmapped params.
+     * @throws SQLException if there are unmapped params.
+     */
+    protected void throwIfUnmappedParams() throws SQLException {        
+        if(paramPosMap.size() != 0) {
+            final Set<String> unmappedParams = paramPosMap.keySet();
+            final StringBuilder sb = new StringBuilder("There are unbound parameters: ");
+            
+            for(String param:unmappedParams) {
+                sb.append(param);
+                sb.append(", ");
+            }
+            
+            // remove the last comma
+            sb.delete(sb.length()-2, sb.length());
+            
+            // throw our exception
+            throw new SQLException(sb.toString());
+        }
+    }
+    
+    /**
+     * Binds a named parameter to a value.
+     * 
+     * @param name the name of the parameter in the SQL statement.
+     * @param value the value of the parameter in the SQL statement.
+     * @return this execution object to provide the fluent style.
+     * @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 {
+        name = name.replace(COLON, ""); // so we can take ":name" or "name"
+
+        final List<Integer> pos = paramPosMap.remove(name);
+        
+        if(pos == null) {
+            throw new SQLException(name + " is either not found in the SQL statement, or was already bound");
+        }
+        
+        // go through and bind all of the positions for this name
+        for(Integer p:pos) {
+            // TODO: need to figure out how to bind NULL
+            stmt.setObject(p, value);
+        }
+        
+        // add the param and value to our map
+        paramValueMap.put(name, value);
+        
+        // suppressed because the casting will always work here
+        @SuppressWarnings("unchecked")
+        final T ret = (T) this;
+        
+        return ret;
+    }
+
+    /**
+     * Throws a new exception with a more informative error message.
+     *
+     * @param cause The original exception that will be chained to the new
+     *              exception when it's rethrown.
+     *
+     * @throws SQLException if a database access error occurs
+     */
+    protected void rethrow(SQLException cause) throws SQLException {
+
+        String causeMessage = cause.getMessage();
+        
+        if (causeMessage == null) {
+            causeMessage = "";
+        }
+        
+        final StringBuffer msg = new StringBuffer(causeMessage);
+
+        msg.append(" Query: ");
+        msg.append(sql);
+        msg.append(" Parameters: ");
+
+        // loop through adding the parameter to value mappings
+        for(Map.Entry<String, Object> param:paramValueMap.entrySet()) {
+            msg.append(param.getKey());
+            msg.append("=");
+            msg.append(param.getValue());
+            msg.append(" ");
+        }
+
+        final SQLException e = new SQLException(msg.toString(), cause.getSQLState(), cause.getErrorCode());
+        e.setNextException(cause);
+
+        throw e;
+    }
+
+    /**
+     * Wrap the <code>ResultSet</code> in a decorator before processing it. This
+     * implementation returns the <code>ResultSet</code> it is given without any
+     * decoration.
+     *
+     * @param rs The <code>ResultSet</code> to decorate; never <code>null</code>.
+     * @return The <code>ResultSet</code> wrapped in some decorator.
+     */
+    protected ResultSet wrap(ResultSet rs) {
+        return rs;
+    }
+
+    /**
+     * Close a <code>Connection</code>. This implementation avoids closing if
+     * null and does <strong>not</strong> suppress any exceptions. Subclasses
+     * can override to provide special handling like logging.
+     *
+     * @param conn Connection to close
+     * @throws SQLException if a database access error occurs
+     */
+    protected void close(Connection conn) throws SQLException {
+        DbUtils.close(conn);
+    }
+
+    /**
+     * Close a <code>Statement</code>. This implementation avoids closing if
+     * null and does <strong>not</strong> suppress any exceptions. Subclasses
+     * can override to provide special handling like logging.
+     *
+     * @param stmt Statement to close
+     * @throws SQLException if a database access error occurs
+     */
+    protected void close(Statement stmt) throws SQLException {
+        DbUtils.close(stmt);
+    }
+
+    /**
+     * Close a <code>ResultSet</code>. This implementation avoids closing if
+     * null and does <strong>not</strong> suppress any exceptions. Subclasses
+     * can override to provide special handling like logging.
+     *
+     * @param rs ResultSet to close
+     * @throws SQLException if a database access error occurs
+     */
+    protected void close(ResultSet rs) throws SQLException {
+        DbUtils.close(rs);
+    }
+
+
+}

Modified: commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java
URL: http://svn.apache.org/viewvc/commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java?rev=1450009&r1=1450008&r2=1450009&view=diff
==============================================================================
--- commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java (original)
+++ commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java Tue Feb 26 03:52:37 2013
@@ -16,35 +16,16 @@
  */
 package org.apache.commons.dbutils;
 
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.sql.Connection;
-import java.sql.ParameterMetaData;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Types;
-import java.util.Arrays;
-
 import javax.sql.DataSource;
 
 /**
  * The base class for QueryRunner & AsyncQueryRunner. This class is thread safe.
  *
- * @since 1.4 (mostly extracted from QueryRunner)
  */
 public abstract class AbstractQueryRunner {
     /**
-     * Is {@link ParameterMetaData#getParameterType(int)} broken (have we tried
-     * it yet)?
-     */
-    private volatile boolean pmdKnownBroken = false;
-
-    /**
      * The DataSource to retrieve connections from.
      */
     private final DataSource ds;
@@ -57,53 +38,17 @@ public abstract class AbstractQueryRunne
     }
 
     /**
-     * Constructor to control the use of <code>ParameterMetaData</code>.
-     *
-     * @param pmdKnownBroken
-     *            Some drivers don't support
-     *            {@link ParameterMetaData#getParameterType(int) }; if
-     *            <code>pmdKnownBroken</code> is set to true, we won't even try
-     *            it; if false, we'll try it, and if it breaks, we'll remember
-     *            not to use it again.
-     */
-    public AbstractQueryRunner(boolean pmdKnownBroken) {
-        this.pmdKnownBroken = pmdKnownBroken;
-        ds = null;
-    }
-
-    /**
      * Constructor to provide a <code>DataSource</code>. Methods that do not
      * take a <code>Connection</code> parameter will retrieve connections from
      * this <code>DataSource</code>.
      *
-     * @param ds
-     *            The <code>DataSource</code> to retrieve connections from.
+     * @param ds The <code>DataSource</code> to retrieve connections from.
      */
     public AbstractQueryRunner(DataSource ds) {
         this.ds = ds;
     }
 
     /**
-     * Constructor to provide a <code>DataSource</code> and control the use of
-     * <code>ParameterMetaData</code>. Methods that do not take a
-     * <code>Connection</code> parameter will retrieve connections from this
-     * <code>DataSource</code>.
-     *
-     * @param ds
-     *            The <code>DataSource</code> to retrieve connections from.
-     * @param pmdKnownBroken
-     *            Some drivers don't support
-     *            {@link ParameterMetaData#getParameterType(int) }; if
-     *            <code>pmdKnownBroken</code> is set to true, we won't even try
-     *            it; if false, we'll try it, and if it breaks, we'll remember
-     *            not to use it again.
-     */
-    public AbstractQueryRunner(DataSource ds, boolean pmdKnownBroken) {
-        this.pmdKnownBroken = pmdKnownBroken;
-        this.ds = ds;
-    }
-
-    /**
      * Returns the <code>DataSource</code> this runner is using.
      * <code>QueryRunner</code> methods always call this method to get the
      * <code>DataSource</code> so subclasses can provide specialized behavior.
@@ -115,44 +60,6 @@ public abstract class AbstractQueryRunne
     }
 
     /**
-     * Some drivers don't support
-     * {@link ParameterMetaData#getParameterType(int) }; if
-     * <code>pmdKnownBroken</code> is set to true, we won't even try it; if
-     * false, we'll try it, and if it breaks, we'll remember not to use it
-     * again.
-     *
-     * @return the flag to skip (or not)
-     *         {@link ParameterMetaData#getParameterType(int) }
-     * @since 1.4
-     */
-    public boolean isPmdKnownBroken() {
-        return pmdKnownBroken;
-    }
-
-    /**
-     * 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)</code>.
-     *
-     * @param conn
-     *            The <code>Connection</code> used to create the
-     *            <code>PreparedStatement</code>
-     * @param sql
-     *            The SQL statement to prepare.
-     * @return An initialized <code>PreparedStatement</code>.
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    protected PreparedStatement prepareStatement(Connection conn, String sql)
-    throws SQLException {
-
-        return conn.prepareStatement(sql);
-    }
-
-    /**
      * Factory method that creates and initializes a <code>Connection</code>
      * object. <code>QueryRunner</code> methods always call this method to
      * retrieve connections from its DataSource. Subclasses can override this
@@ -160,9 +67,7 @@ public abstract class AbstractQueryRunne
      * needed. This implementation simply calls <code>ds.getConnection()</code>.
      *
      * @return An initialized <code>Connection</code>.
-     * @throws SQLException
-     *             if a database access error occurs
-     * @since DbUtils 1.1
+     * @throws SQLException if a database access error occurs
      */
     protected Connection prepareConnection() throws SQLException {
         if (this.getDataSource() == null) {
@@ -175,78 +80,18 @@ public abstract class AbstractQueryRunne
 
     /**
      * Fill the <code>PreparedStatement</code> replacement parameters with the
-     * given objects.
-     *
-     * @param stmt
-     *            PreparedStatement to fill
-     * @param params
-     *            Query replacement parameters; <code>null</code> is a valid
-     *            value to pass in.
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    public void fillStatement(PreparedStatement stmt, Object... params)
-    throws SQLException {
-
-        // check the parameter count, if we can
-        ParameterMetaData pmd = null;
-        if (!pmdKnownBroken) {
-            pmd = stmt.getParameterMetaData();
-            int stmtCount = pmd.getParameterCount();
-            int paramsCount = params == null ? 0 : params.length;
-
-            if (stmtCount != paramsCount) {
-                throw new SQLException("Wrong number of parameters: expected "
-                        + stmtCount + ", was given " + paramsCount);
-            }
-        }
-
-        // nothing to do here
-        if (params == null) {
-            return;
-        }
-
-        for (int i = 0; i < params.length; i++) {
-            if (params[i] != null) {
-                stmt.setObject(i + 1, params[i]);
-            } else {
-                // VARCHAR works with many drivers regardless
-                // of the actual column type. Oddly, NULL and
-                // OTHER don't work with Oracle's drivers.
-                int sqlType = Types.VARCHAR;
-                if (!pmdKnownBroken) {
-                    try {
-                        /*
-                         * It's not possible for pmdKnownBroken to change from
-                         * true to false, (once true, always true) so pmd cannot
-                         * be null here.
-                         */
-                        sqlType = pmd.getParameterType(i + 1);
-                    } catch (SQLException e) {
-                        pmdKnownBroken = true;
-                    }
-                }
-                stmt.setNull(i + 1, sqlType);
-            }
-        }
-    }
-
-    /**
-     * Fill the <code>PreparedStatement</code> replacement parameters with the
      * given object's bean property values.
      *
-     * @param stmt
-     *            PreparedStatement to fill
-     * @param bean
-     *            a JavaBean object
-     * @param properties
-     *            an ordered array of properties; this gives the order to insert
-     *            values in the statement
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    public void fillStatementWithBean(PreparedStatement stmt, Object bean,
-            PropertyDescriptor[] properties) throws SQLException {
+     * @param stmt PreparedStatement to fill
+     * @param bean a JavaBean object
+     * @param properties an ordered array of properties; this gives the order to insert
+     *                   values in the statement
+     * @throws SQLException if a database access error occurs
+     */
+/*
+    public void fillStatementWithBean(PreparedStatement stmt,
+                                      Object bean,
+                                      PropertyDescriptor[] properties) throws SQLException {
         Object[] params = new Object[properties.length];
         for (int i = 0; i < properties.length; i++) {
             PropertyDescriptor property = properties[i];
@@ -272,6 +117,7 @@ public abstract class AbstractQueryRunne
         }
         fillStatement(stmt, params);
     }
+*/
 
     /**
      * Fill the <code>PreparedStatement</code> replacement parameters with the
@@ -288,6 +134,7 @@ public abstract class AbstractQueryRunne
      * @throws SQLException
      *             If a database access error occurs
      */
+/*
     public void fillStatementWithBean(PreparedStatement stmt, Object bean,
             String... propertyNames) throws SQLException {
         PropertyDescriptor[] descriptors;
@@ -321,120 +168,18 @@ public abstract class AbstractQueryRunne
         }
         fillStatementWithBean(stmt, bean, sorted);
     }
-
-    /**
-     * Throws a new exception with a more informative error message.
-     *
-     * @param cause
-     *            The original exception that will be chained to the new
-     *            exception when it's rethrown.
-     *
-     * @param sql
-     *            The query that was executing when the exception happened.
-     *
-     * @param params
-     *            The query replacement parameters; <code>null</code> is a valid
-     *            value to pass in.
-     *
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    protected void rethrow(SQLException cause, String sql, Object... params)
-    throws SQLException {
-
-        String causeMessage = cause.getMessage();
-        if (causeMessage == null) {
-            causeMessage = "";
-        }
-        StringBuffer msg = new StringBuffer(causeMessage);
-
-        msg.append(" Query: ");
-        msg.append(sql);
-        msg.append(" Parameters: ");
-
-        if (params == null) {
-            msg.append("[]");
-        } else {
-            msg.append(Arrays.deepToString(params));
-        }
-
-        SQLException e = new SQLException(msg.toString(), cause.getSQLState(),
-                cause.getErrorCode());
-        e.setNextException(cause);
-
-        throw e;
-    }
-
-    /**
-     * Wrap the <code>ResultSet</code> in a decorator before processing it. This
-     * implementation returns the <code>ResultSet</code> it is given without any
-     * decoration.
-     *
-     * <p>
-     * Often, the implementation of this method can be done in an anonymous
-     * inner class like this:
-     * </p>
-     *
-     * <pre>
-     * QueryRunner run = new QueryRunner() {
-     *     protected ResultSet wrap(ResultSet rs) {
-     *         return StringTrimmedResultSet.wrap(rs);
-     *     }
-     * };
-     * </pre>
-     *
-     * @param rs
-     *            The <code>ResultSet</code> to decorate; never
-     *            <code>null</code>.
-     * @return The <code>ResultSet</code> wrapped in some decorator.
-     */
-    protected ResultSet wrap(ResultSet rs) {
-        return rs;
-    }
-
+*/
+    
     /**
      * Close a <code>Connection</code>. This implementation avoids closing if
      * null and does <strong>not</strong> suppress any exceptions. Subclasses
      * can override to provide special handling like logging.
      *
-     * @param conn
-     *            Connection to close
-     * @throws SQLException
-     *             if a database access error occurs
-     * @since DbUtils 1.1
+     * @param conn Connection to close
+     * @throws SQLException if a database access error occurs
      */
     protected void close(Connection conn) throws SQLException {
         DbUtils.close(conn);
     }
 
-    /**
-     * Close a <code>Statement</code>. This implementation avoids closing if
-     * null and does <strong>not</strong> suppress any exceptions. Subclasses
-     * can override to provide special handling like logging.
-     *
-     * @param stmt
-     *            Statement to close
-     * @throws SQLException
-     *             if a database access error occurs
-     * @since DbUtils 1.1
-     */
-    protected void close(Statement stmt) throws SQLException {
-        DbUtils.close(stmt);
-    }
-
-    /**
-     * Close a <code>ResultSet</code>. This implementation avoids closing if
-     * null and does <strong>not</strong> suppress any exceptions. Subclasses
-     * can override to provide special handling like logging.
-     *
-     * @param rs
-     *            ResultSet to close
-     * @throws SQLException
-     *             if a database access error occurs
-     * @since DbUtils 1.1
-     */
-    protected void close(ResultSet rs) throws SQLException {
-        DbUtils.close(rs);
-    }
-
 }

Modified: commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.java
URL: http://svn.apache.org/viewvc/commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.java?rev=1450009&r1=1450008&r2=1450009&view=diff
==============================================================================
--- commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.java (original)
+++ commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.java Tue Feb 26 03:52:37 2013
@@ -16,364 +16,82 @@
  */
 package org.apache.commons.dbutils;
 
-import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 
 /**
- * Executes SQL queries with pluggable strategies for handling
- * <code>ResultSet</code>s.  This class is thread safe.
+ * Convenience class for executing QueryExecutor, InsertExecutor, or UpdateExecutors asynchronously.
  *
- * @see ResultSetHandler
- * @since 1.4
+ * @author William Speirs <ws...@apache.org>
+ * @since 2.0
  */
-public class AsyncQueryRunner extends AbstractQueryRunner {
+public class AsyncQueryRunner {
 
     private final ExecutorService executorService;
-    private final QueryRunner queryRunner;
 
     /**
      * Constructor for AsyncQueryRunner which uses a provided ExecutorService and underlying QueryRunner.
      *
      * @param executorService the {@code ExecutorService} instance used to run JDBC invocations concurrently.
      * @param queryRunner the {@code QueryRunner} instance to use for the queries.
-     * @since DbUtils 1.5
      */
-    public AsyncQueryRunner(ExecutorService executorService, QueryRunner queryRunner) {
+    public AsyncQueryRunner(ExecutorService executorService) {
         this.executorService = executorService;
-        this.queryRunner = queryRunner;
     }
 
     /**
-     * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
-     *
-     * @param conn The <code>Connection</code> 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 A <code>Future</code> which returns the number of rows updated per statement.
-     * @throws SQLException if a database access error occurs
-     */
-    public Future<int[]> batch(final Connection conn, final String sql, final Object[][] params) throws SQLException {
-        return executorService.submit(new Callable<int[]>() {
-
-            @Override
-            public int[] call() throws Exception {
-                return queryRunner.batch(conn, sql, params);
-            }
-
-        });
-    }
-
-    /**
-     * 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.
-     *
-     * @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 A <code>Future</code> which returns the number of rows updated per statement.
-     * @throws SQLException if a database access error occurs
-     */
-    public Future<int[]> batch(final String sql, final Object[][] params) throws SQLException {
-        return executorService.submit(new Callable<int[]>() {
-
-            @Override
-            public int[] call() throws Exception {
-                return queryRunner.batch(sql, params);
-            }
-
-        });
-    }
-
-    /**
-     * Execute an SQL SELECT query with replacement parameters.  The
-     * caller is responsible for closing the connection.
-     * @param <T> The type of object that the handler returns
-     * @param conn The connection to execute the query in.
-     * @param sql The query to execute.
-     * @param rsh The handler that converts the results into an object.
-     * @param params The replacement parameters.
-     * @return A <code>Future</code> which returns the result of the query call.
-     * @throws SQLException if a database access error occurs
-     */
-    public <T> Future<T> query(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.query(conn, sql, rsh, params);
-            }
-
-        });
-    }
-
-    /**
-     * Execute an SQL SELECT query without any replacement parameters.  The
-     * caller is responsible for closing the connection.
-     * @param <T> The type of object that the handler returns
-     * @param conn The connection to execute the query in.
-     * @param sql The query to execute.
-     * @param rsh The handler that converts the results into an object.
-     * @return A <code>Future</code> which returns the result of the query call.
-     * @throws SQLException if a database access error occurs
-     */
-    public <T> Future<T> query(final Connection conn, final String sql, final ResultSetHandler<T> rsh) throws SQLException {
-        return executorService.submit(new Callable<T>() {
-
-            @Override
-            public T call() throws Exception {
-                return queryRunner.query(conn, sql, rsh);
-            }
-
-        });
-    }
-
-    /**
-     * Executes the given SELECT SQL query and returns a result object.
-     * The <code>Connection</code> is retrieved from the
-     * <code>DataSource</code> set in the constructor.
+     * Execute a {@link org.apache.commons.dbutils.QueryExecutor} given a handler.
      * @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>.
-     * @param params Initialize the PreparedStatement's IN parameters with
-     * this array.
+     * @param handler The handler that converts the results into an object.
      * @return A <code>Future</code> which returns the result of the query call.
      * @throws SQLException if a database access error occurs
      */
-    public <T> Future<T> query(final String sql, final ResultSetHandler<T> rsh, final Object... params) throws SQLException {
+    public <T> Future<T> query(final QueryExecutor executor, final ResultSetHandler<T> handler) throws SQLException {
         return executorService.submit(new Callable<T>() {
 
             @Override
             public T call() throws Exception {
-                return queryRunner.query(sql, rsh, params);
+                return executor.query(handler);
             }
 
         });
     }
 
     /**
-     * Executes the given SELECT SQL without any replacement parameters.
-     * The <code>Connection</code> is retrieved from the
-     * <code>DataSource</code> set in the constructor.
+     * Execute a {@link org.apache.commons.dbutils.UpdateExecutor} given a handler.
      * @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>.
-     *
      * @return A <code>Future</code> which returns the result of the query call.
      * @throws SQLException if a database access error occurs
      */
-    public <T> Future<T> query(final String sql, final ResultSetHandler<T> rsh) throws SQLException {
-        return executorService.submit(new Callable<T>() {
-
-            @Override
-            public T call() throws Exception {
-                return queryRunner.query(sql, rsh);
-            }
-
-        });
-    }
-
-    /**
-     * Execute an SQL INSERT, UPDATE, or DELETE query without replacement
-     * parameters.
-     *
-     * @param conn The connection to use to run the query.
-     * @param sql The SQL to execute.
-     * @return A <code>Future</code> which returns the number of rows updated.
-     * @throws SQLException if a database access error occurs
-     */
-    public Future<Integer> update(final Connection conn, final String sql) throws SQLException {
-        return executorService.submit(new Callable<Integer>() {
-
-            @Override
-            public Integer call() throws Exception {
-                return queryRunner.update(conn, sql);
-            }
-
-        });
-    }
-
-    /**
-     * Execute an SQL INSERT, UPDATE, or DELETE query with a single replacement
-     * parameter.
-     *
-     * @param conn The connection to use to run the query.
-     * @param sql The SQL to execute.
-     * @param param The replacement parameter.
-     * @return A <code>Future</code> which returns the number of rows updated.
-     * @throws SQLException if a database access error occurs
-     */
-    public Future<Integer> update(final Connection conn, final String sql, final Object param) throws SQLException {
-        return executorService.submit(new Callable<Integer>() {
-
-            @Override
-            public Integer call() throws Exception {
-                return queryRunner.update(conn, sql, param);
-            }
-
-        });
-    }
-
-    /**
-     * Execute an SQL INSERT, UPDATE, or DELETE query.
-     *
-     * @param conn The connection to use to run the query.
-     * @param sql The SQL to execute.
-     * @param params The query replacement parameters.
-     * @return A <code>Future</code> which returns the number of rows updated.
-     * @throws SQLException if a database access error occurs
-     */
-    public Future<Integer> update(final Connection conn, final String sql, final Object... params) throws SQLException {
-        return executorService.submit(new Callable<Integer>() {
-
-            @Override
-            public Integer call() throws Exception {
-                return queryRunner.update(conn, sql, params);
-            }
-
-        });
-    }
-
-    /**
-     * Executes the given INSERT, UPDATE, or DELETE SQL statement without
-     * any replacement parameters. 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.
-     *
-     * @param sql The SQL statement to execute.
-     * @throws SQLException if a database access error occurs
-     * @return A <code>Future</code> which returns the number of rows updated.
-     */
-    public Future<Integer> update(final String sql) throws SQLException {
+    public Future<Integer> update(final UpdateExecutor executor) throws SQLException {
         return executorService.submit(new Callable<Integer>() {
 
             @Override
             public Integer call() throws Exception {
-                return queryRunner.update(sql);
+                return executor.update();
             }
 
         });
     }
 
     /**
-     * Executes the given INSERT, UPDATE, or DELETE SQL statement with
-     * a single replacement parameter.  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.
-     *
-     * @param sql The SQL statement to execute.
-     * @param param The replacement parameter.
-     * @throws SQLException if a database access error occurs
-     * @return A <code>Future</code> which returns the number of rows updated.
-     */
-    public Future<Integer> update(final String sql, final Object param) throws SQLException {
-        return executorService.submit(new Callable<Integer>() {
-
-            @Override
-            public Integer call() throws Exception {
-                return queryRunner.update(sql, param);
-            }
-
-        });
-    }
-
-    /**
-     * Executes the given INSERT, UPDATE, or DELETE SQL statement.  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.
-     *
-     * @param sql The SQL statement to execute.
-     * @param params Initializes the PreparedStatement's IN (i.e. '?')
-     * parameters.
+     * Execute a {@link org.apache.commons.dbutils.InsertExecutor} given a handler.
+     * @param <T> The type of object that the handler returns
+     * @param handler The handler that converts the results into an object.
+     * @return A <code>Future</code> which returns the result of the query call.
      * @throws SQLException if a database access error occurs
-     * @return A <code>Future</code> which returns the number of rows updated.
-     */
-    public Future<Integer> update(final String sql, final Object... params) throws SQLException {
-        return executorService.submit(new Callable<Integer>() {
-
-            @Override
-            public Integer call() throws Exception {
-                return queryRunner.update(sql, params);
-            }
-
-        });
-    }
-
-    /**
-     * Executes {@link QueryRunner#insert(String, ResultSetHandler)} asynchronously.
-     *
-     * @see QueryRunner#insert(String, ResultSetHandler)
-     * @since 1.6
      */
-    public <T> Future<T> insert(final String sql, final ResultSetHandler<T> rsh) throws SQLException {
+    public <T> Future<T> insert(final InsertExecutor executor, final ResultSetHandler<T> handler) throws SQLException {
         return executorService.submit(new Callable<T>() {
 
             @Override
             public T call() throws Exception {
-                return queryRunner.insert(sql, rsh);
+                return executor.insert(handler);
             }
 
         });
     }
 
-    /**
-     * Executes {@link QueryRunner#insert(String, ResultSetHandler, Object...)} asynchronously.
-     *
-     * @see QueryRunner#insert(String, ResultSetHandler, Object...)
-     * @since 1.6
-     */
-    public <T> Future<T> insert(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.insert(sql, rsh, params);
-            }
-        });
-    }
-
-    /**
-     * Executes {@link QueryRunner#insert(Connection, String, ResultSetHandler)} asynchronously.
-     *
-     * @see QueryRunner#insert(Connection, String, ResultSetHandler)
-     * @since 1.6
-     */
-    public <T> Future<T> insert(final Connection conn, final String sql, final ResultSetHandler<T> rsh) throws SQLException {
-        return executorService.submit(new Callable<T>() {
-
-            @Override
-            public T call() throws Exception {
-                return queryRunner.insert(conn, sql, rsh);
-            }
-        });
-    }
-
-    /**
-     * Executes {@link QueryRunner#insert(Connection, String, ResultSetHandler, Object...)} asynchronously.
-     *
-     * @see QueryRunner#insert(Connection, String, ResultSetHandler, Object...)
-     * @since 1.6
-     */
-    public <T> Future<T> insert(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.insert(conn, sql, rsh, params);
-            }
-        });
-    }
-
 }

Added: commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/InsertExecutor.java
URL: http://svn.apache.org/viewvc/commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/InsertExecutor.java?rev=1450009&view=auto
==============================================================================
--- commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/InsertExecutor.java (added)
+++ commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/InsertExecutor.java Tue Feb 26 03:52:37 2013
@@ -0,0 +1,76 @@
+/*
+ * 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 java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+
+public class InsertExecutor extends AbstractExecutor<InsertExecutor> {
+
+    private final boolean closeConn;
+
+    public InsertExecutor(final Connection conn, final String sql, final boolean closeConnection) throws SQLException {
+        super(conn, sql);
+        this.closeConn = closeConnection;
+    }
+
+    /**
+     * Executes the given INSERT SQL statement.
+     * 
+     * @param handler The handler used to create the result object from
+     * the <code>ResultSet</code> of auto-generated keys.
+     *
+     * @return An object generated by the handler.
+     * @throws SQLException If there are database or parameter errors.
+     */
+    public <T> T insert(ResultSetHandler<T> handler) throws SQLException {
+        // throw an exception if there are unmapped parameters
+        this.throwIfUnmappedParams();
+
+        // make sure our handler is not null
+        if (handler == null) {
+            if (closeConn) {
+                close(getConnection());
+            }
+            throw new SQLException("Null ResultSetHandler");
+        }
+
+        try {
+            // execute the update
+            getStatement().executeUpdate();
+            
+            // get the result set
+            final ResultSet resultSet = getStatement().getGeneratedKeys();
+            
+            // run the handler over the results and return them
+            return handler.handle(resultSet);
+        } catch (SQLException e) {
+            this.rethrow(e);
+        } finally {
+            close(getStatement());
+            if (closeConn) {
+                close(getConnection());
+            }
+        }
+
+        // we get here only if something is thrown
+        return null;
+    }
+    
+}

Added: commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/QueryExecutor.java
URL: http://svn.apache.org/viewvc/commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/QueryExecutor.java?rev=1450009&view=auto
==============================================================================
--- commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/QueryExecutor.java (added)
+++ commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/QueryExecutor.java Tue Feb 26 03:52:37 2013
@@ -0,0 +1,82 @@
+/*
+ * 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 java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * Fluent class for executing a query.
+ * 
+ * @since 2.0
+ * @author William Speirs <ws...@apache.org>
+ */
+class QueryExecutor extends AbstractExecutor<QueryExecutor> {
+    
+    private final boolean closeConn;
+
+    public QueryExecutor(final Connection conn, final String sql, final boolean closeConnection) throws SQLException {
+        super(conn, sql);
+        this.closeConn = closeConnection;
+    }
+
+    /**
+     * Calls query after checking the parameters to ensure nothing is null.
+     *
+     * @param rsh The handler that converts the results into an object.
+     *
+     * @return The results of the query.
+     * @throws SQLException If there are database or parameter errors.
+     */
+    public <T> T query(ResultSetHandler<T> handler) throws SQLException {
+        // throw an exception if there are unmapped parameters
+        this.throwIfUnmappedParams();
+        
+        // make sure our handler is not null
+        if (handler == null) {
+            if (closeConn) {
+                close(getConnection());
+            }
+            throw new SQLException("Null ResultSetHandler");
+        }
+
+        ResultSet resultSet = null;
+
+        try {
+            // execute the query, wrapping it
+            resultSet = this.wrap(getStatement().executeQuery());
+            // execute the handler
+            return handler.handle(resultSet);
+        } catch (SQLException e) {
+            // rethrow our exception printing more information
+            this.rethrow(e);
+        } finally {
+            try {
+                close(resultSet);
+            } 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=1450009&r1=1450008&r2=1450009&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 03:52:37 2013
@@ -17,11 +17,7 @@
 package org.apache.commons.dbutils;
 
 import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Statement;
-
 import javax.sql.DataSource;
 
 /**
@@ -40,17 +36,6 @@ public class QueryRunner extends Abstrac
     }
 
     /**
-     * Constructor for QueryRunner that controls the use of <code>ParameterMetaData</code>.
-     *
-     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
-     * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
-     * and if it breaks, we'll remember not to use it again.
-     */
-    public QueryRunner(boolean pmdKnownBroken) {
-        super(pmdKnownBroken);
-    }
-
-    /**
      * Constructor for QueryRunner that takes a <code>DataSource</code> to use.
      *
      * Methods that do not take a <code>Connection</code> parameter will retrieve connections from this
@@ -63,20 +48,6 @@ public class QueryRunner extends Abstrac
     }
 
     /**
-     * Constructor for QueryRunner that takes a <code>DataSource</code> and controls the use of <code>ParameterMetaData</code>.
-     * Methods that do not take a <code>Connection</code> parameter will retrieve connections from this
-     * <code>DataSource</code>.
-     *
-     * @param ds The <code>DataSource</code> to retrieve connections from.
-     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
-     * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
-     * and if it breaks, we'll remember not to use it again.
-     */
-    public QueryRunner(DataSource ds, boolean pmdKnownBroken) {
-        super(ds, pmdKnownBroken);
-    }
-
-    /**
      * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
      *
      * @param conn The Connection to use to run the query.  The caller is
@@ -122,6 +93,8 @@ public class QueryRunner extends Abstrac
      * @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");
+/*        
         if (conn == null) {
             throw new SQLException("Null connection");
         }
@@ -161,86 +134,46 @@ public class QueryRunner extends Abstrac
         }
 
         return rows;
+*/
     }
 
     /**
-     * Execute an SQL SELECT query with replacement parameters.  The
-     * caller is responsible for closing the connection.
-     * @param <T> The type of object that the handler returns
-     * @param conn The connection to execute the query in.
-     * @param sql The query to execute.
-     * @param rsh The handler that converts the results into an object.
-     * @param params The replacement parameters.
-     * @return The object returned by the handler.
-     * @throws SQLException if a database access error occurs
-     */
-    public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
-        return this.<T>query(conn, false, sql, rsh, params);
-    }
-
-    /**
-     * Execute an SQL SELECT query without any replacement parameters.  The
-     * caller is responsible for closing the connection.
-     * @param <T> The type of object that the handler returns
-     * @param conn The connection to execute the query in.
-     * @param sql The query to execute.
-     * @param rsh The handler that converts the results into an object.
-     * @return The object returned by the handler.
-     * @throws SQLException if a database access error occurs
-     */
-    public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh) throws SQLException {
-        return this.<T>query(conn, false, sql, rsh, (Object[]) null);
-    }
-
-    /**
-     * Executes the given SELECT SQL query and returns a result object.
+     * 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.
-     * @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>.
-     * @param params Initialize the PreparedStatement's IN parameters with
-     * this array.
-     * @return An object generated by the handler.
-     * @throws SQLException if a database access error occurs
+     * @return An {@link org.apache.commons.dbutils.QueryExecutor} for this SQL statement.
+     * @throws SQLException If there are database or parameter errors.
      */
-    public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
+    public QueryExecutor query(String sql) throws SQLException {
         Connection conn = this.prepareConnection();
 
-        return this.<T>query(conn, true, sql, rsh, params);
+        return this.query(conn, true, sql);
     }
 
     /**
-     * Executes the given SELECT SQL without any replacement parameters.
-     * The <code>Connection</code> is retrieved from the
-     * <code>DataSource</code> set in the constructor.
-     * @param <T> The type of object that the handler returns
+     * 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.
-     * @param rsh The handler used to create the result object from
-     * the <code>ResultSet</code>.
-     *
-     * @return An object generated by the handler.
-     * @throws SQLException if a database access error occurs
+     * @return An {@link org.apache.commons.dbutils.QueryExecutor} for this SQL statement.
+     * @throws SQLException If there are database or parameter errors.
      */
-    public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
-        Connection conn = this.prepareConnection();
+    public QueryExecutor query(Connection con, String sql) throws SQLException {
+        final Connection conn = this.prepareConnection();
 
-        return this.<T>query(conn, true, sql, rsh, (Object[]) null);
+        return this.query(conn, true, sql);
     }
 
     /**
-     * Calls query after checking the parameters to ensure nothing is null.
-     * @param conn The connection to use for the query call.
+     * 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 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 results of the query.
+     * @return An {@link org.apache.commons.dbutils.QueryExecutor} for this SQL statement.
      * @throws SQLException If there are database or parameter errors.
      */
-    private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params)
-            throws SQLException {
+    public QueryExecutor query(Connection conn, boolean closeConn, String sql) throws SQLException {
         if (conn == null) {
             throw new SQLException("Null connection");
         }
@@ -251,145 +184,47 @@ public class QueryRunner extends Abstrac
             }
             throw new SQLException("Null SQL statement");
         }
-
-        if (rsh == null) {
-            if (closeConn) {
-                close(conn);
-            }
-            throw new SQLException("Null ResultSetHandler");
-        }
-
-        PreparedStatement stmt = null;
-        ResultSet rs = null;
-        T result = null;
-
-        try {
-            stmt = this.prepareStatement(conn, sql);
-            this.fillStatement(stmt, params);
-            rs = this.wrap(stmt.executeQuery());
-            result = rsh.handle(rs);
-
-        } catch (SQLException e) {
-            this.rethrow(e, sql, params);
-
-        } finally {
-            try {
-                close(rs);
-            } finally {
-                close(stmt);
-                if (closeConn) {
-                    close(conn);
-                }
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Execute an SQL INSERT, UPDATE, or DELETE query without replacement
-     * parameters.
-     *
-     * @param conn The connection to use to run the query.
-     * @param sql The SQL to execute.
-     * @return The number of rows updated.
-     * @throws SQLException if a database access error occurs
-     */
-    public int update(Connection conn, String sql) throws SQLException {
-        return this.update(conn, false, sql, (Object[]) null);
-    }
-
-    /**
-     * Execute an SQL INSERT, UPDATE, or DELETE query with a single replacement
-     * parameter.
-     *
-     * @param conn The connection to use to run the query.
-     * @param sql The SQL to execute.
-     * @param param The replacement parameter.
-     * @return The number of rows updated.
-     * @throws SQLException if a database access error occurs
-     */
-    public int update(Connection conn, String sql, Object param) throws SQLException {
-        return this.update(conn, false, sql, new Object[]{param});
-    }
-
-    /**
-     * Execute an SQL INSERT, UPDATE, or DELETE query.
-     *
-     * @param conn The connection to use to run the query.
-     * @param sql The SQL to execute.
-     * @param params The query replacement parameters.
-     * @return The number of rows updated.
-     * @throws SQLException if a database access error occurs
-     */
-    public int update(Connection conn, String sql, Object... params) throws SQLException {
-        return update(conn, false, sql, params);
-    }
-
-    /**
-     * Executes the given INSERT, UPDATE, or DELETE SQL statement without
-     * any replacement parameters. 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.
-     *
-     * @param sql The SQL statement to execute.
-     * @throws SQLException if a database access error occurs
-     * @return The number of rows updated.
-     */
-    public int update(String sql) throws SQLException {
-        Connection conn = this.prepareConnection();
-
-        return this.update(conn, true, sql, (Object[]) null);
+        
+        return new QueryExecutor(conn, sql, closeConn);
     }
 
     /**
-     * Executes the given INSERT, UPDATE, or DELETE SQL statement with
-     * a single replacement parameter.  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.UpdateExecutor} for the given SQL statement.
+     * <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.
      *
      * @param sql The SQL statement to execute.
-     * @param param The replacement parameter.
+     * @return An {@link org.apache.commons.dbutils.UpdateExecutor} for this SQL statement.
      * @throws SQLException if a database access error occurs
-     * @return The number of rows updated.
      */
-    public int update(String sql, Object param) throws SQLException {
+    public UpdateExecutor update(String sql) throws SQLException {
         Connection conn = this.prepareConnection();
 
-        return this.update(conn, true, sql, new Object[]{param});
+        return this.update(conn, true, sql);
     }
 
     /**
-     * Executes the given INSERT, UPDATE, or DELETE SQL statement.  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.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.
-     * @param params Initializes the PreparedStatement's IN (i.e. '?')
-     * parameters.
-     * @throws SQLException if a database access error occurs
-     * @return The number of rows updated.
+     * @return An {@link org.apache.commons.dbutils.UpdateExecutor} for this SQL statement.
+     * @throws SQLException If there are database or parameter errors.
      */
-    public int update(String sql, Object... params) throws SQLException {
-        Connection conn = this.prepareConnection();
-
-        return this.update(conn, true, sql, params);
+    public UpdateExecutor update(Connection conn, String sql) throws SQLException {
+        return this.update(conn, false, sql);
     }
 
     /**
-     * Calls update after checking the parameters to ensure nothing is null.
+     * 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.
-     * @param params An array of update replacement parameters.  Each row in
-     * this array is one set of update replacement values.
-     * @return The number of rows updated.
+     * @return An {@link org.apache.commons.dbutils.UpdateExecutor} for this SQL statement.
      * @throws SQLException If there are database or parameter errors.
      */
-    private int update(Connection conn, boolean closeConn, String sql, Object... params) throws SQLException {
+    public UpdateExecutor update(Connection conn, boolean closeConn, String sql) throws SQLException {
         if (conn == null) {
             throw new SQLException("Null connection");
         }
@@ -401,105 +236,47 @@ public class QueryRunner extends Abstrac
             throw new SQLException("Null SQL statement");
         }
 
-        PreparedStatement stmt = null;
-        int rows = 0;
-
-        try {
-            stmt = this.prepareStatement(conn, sql);
-            this.fillStatement(stmt, params);
-            rows = stmt.executeUpdate();
-
-        } catch (SQLException e) {
-            this.rethrow(e, sql, params);
-
-        } finally {
-            close(stmt);
-            if (closeConn) {
-                close(conn);
-            }
-        }
-
-        return rows;
+        return new UpdateExecutor(conn, sql, closeConn);
     }
 
     /**
-     * Executes the given INSERT SQL without any replacement parameters.
-     * The <code>Connection</code> is retrieved from the
-     * <code>DataSource</code> set in the constructor.
-     * @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.
-     * @return An object generated by the handler.
-     * @throws SQLException if a database access error occurs
-     * @since 1.6
-     */
-    public <T> T insert(String sql, ResultSetHandler<T> rsh) throws SQLException {
-        return insert(this.prepareConnection(), true, sql, rsh, (Object[]) null);
-    }
-
-    /**
-     * Executes the given INSERT SQL statement. The
+     * 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 <T> The type of object that the handler returns
+     * @param conn The connection to use for the query call.
      * @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 An object generated by the handler.
-     * @throws SQLException if a database access error occurs
-     * @since 1.6
-     */
-    public <T> T insert(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
-        return insert(this.prepareConnection(), true, sql, rsh, params);
-    }
-
-    /**
-     * Execute an SQL INSERT query without replacement parameters.
-     * @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.
-     * @return An object generated by the handler.
-     * @throws SQLException if a database access error occurs
-     * @since 1.6
+     *
+     * @return An {@link org.apache.commons.dbutils.InsertExecutor} for this SQL statement.
+     * @throws SQLException If there are database or parameter errors.
      */
-    public <T> T insert(Connection conn, String sql, ResultSetHandler<T> rsh) throws SQLException {
-        return insert(conn, false, sql, rsh, (Object[]) null);
+    public InsertExecutor insert(String sql) throws SQLException {
+        return insert(this.prepareConnection(), true, sql);
     }
 
     /**
-     * Execute an SQL INSERT query.
-     * @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 An object generated by the handler.
-     * @throws SQLException if a database access error occurs
-     * @since 1.6
+     * 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.
+     *
+     * @return An {@link org.apache.commons.dbutils.InsertExecutor} for this SQL statement.
+     * @throws SQLException If there are database or parameter errors.
      */
-    public <T> T insert(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
-        return insert(conn, false, sql, rsh, params);
+    public InsertExecutor insert(Connection conn, String sql) throws SQLException {
+        return insert(conn, false, sql);
     }
 
     /**
-     * Executes the given INSERT SQL statement.
+     * 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 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 An object generated by the handler.
+     *
+     * @return An {@link org.apache.commons.dbutils.InsertExecutor} for this SQL statement.
      * @throws SQLException If there are database or parameter errors.
-     * @since 1.6
      */
-    private <T> T insert(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
+    public InsertExecutor insert(Connection conn, boolean closeConn, String sql) throws SQLException {
         if (conn == null) {
             throw new SQLException("Null connection");
         }
@@ -510,32 +287,7 @@ public class QueryRunner extends Abstrac
             }
             throw new SQLException("Null SQL statement");
         }
-
-        if (rsh == null) {
-            if (closeConn) {
-                close(conn);
-            }
-            throw new SQLException("Null ResultSetHandler");
-        }
-
-        PreparedStatement stmt = null;
-        T generatedKeys = null;
-
-        try {
-            stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
-            this.fillStatement(stmt, params);
-            stmt.executeUpdate();
-            ResultSet resultSet = stmt.getGeneratedKeys();
-            generatedKeys = rsh.handle(resultSet);
-        } catch (SQLException e) {
-            this.rethrow(e, sql, params);
-        } finally {
-            close(stmt);
-            if (closeConn) {
-                close(conn);
-            }
-        }
-
-        return generatedKeys;
+        
+        return new InsertExecutor(conn, sql, closeConn);
     }
 }

Added: commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/UpdateExecutor.java
URL: http://svn.apache.org/viewvc/commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/UpdateExecutor.java?rev=1450009&view=auto
==============================================================================
--- commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/UpdateExecutor.java (added)
+++ commons/proper/dbutils/branches/2_0/src/main/java/org/apache/commons/dbutils/UpdateExecutor.java Tue Feb 26 03:52:37 2013
@@ -0,0 +1,41 @@
+package org.apache.commons.dbutils;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+
+public class UpdateExecutor extends AbstractExecutor<UpdateExecutor> {
+
+    private final boolean closeConn;
+    
+    public UpdateExecutor(final Connection conn, final String sql, final boolean closeConnection) throws SQLException {
+        super(conn, sql);
+        this.closeConn = closeConnection;
+    }
+
+    /**
+     * Calls update after checking the parameters to ensure nothing is null.
+     * @return The number of rows updated.
+     * @throws SQLException If there are database or parameter errors.
+     */
+    public int update() throws SQLException {
+        // throw an exception if there are unmapped parameters
+        this.throwIfUnmappedParams();
+
+        try {
+            return getStatement().executeUpdate();
+        } catch (SQLException e) {
+            this.rethrow(e);
+
+        } finally {
+            close(getStatement());
+            if (closeConn) {
+                close(getConnection());
+            }
+        }
+
+        // we get here only if something is thrown
+        return 0;
+    }
+
+}

Added: commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/AbstractExecutorTest.java
URL: http://svn.apache.org/viewvc/commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/AbstractExecutorTest.java?rev=1450009&view=auto
==============================================================================
--- commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/AbstractExecutorTest.java (added)
+++ commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/AbstractExecutorTest.java Tue Feb 26 03:52:37 2013
@@ -0,0 +1,108 @@
+/*
+ * 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.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+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 AbstractExecutorTest {
+
+    @SuppressWarnings("rawtypes") // don't care about this in the unit test
+    private AbstractExecutor 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);
+    }
+    
+    @SuppressWarnings("rawtypes")
+    public void createExecutor(String sql) throws SQLException {
+        executor = new AbstractExecutor(conn, sql) { };
+    }
+    
+    @Test
+    public void testGoodSql() throws SQLException {
+        createExecutor("select * from blah :first = first and :last=last and phone=:phone");
+
+        verify(conn, times(1)).prepareStatement("select * from blah ? = first and ?=last and phone=?");
+
+        executor.bind("first", "first_name")
+                .bind(":last", "last_name")
+                .bind("phone", Integer.valueOf(12345));
+       
+        verify(stmt, times(1)).setObject(1, "first_name");
+        verify(stmt, times(1)).setObject(2, "last_name");
+        verify(stmt, times(1)).setObject(eq(3), eq(Integer.valueOf(12345)));
+        
+        executor.throwIfUnmappedParams();
+    }
+
+    @Test
+    public void testNoParamsSql() throws SQLException {
+        createExecutor("select * from blah");
+
+        verify(conn, times(1)).prepareStatement("select * from blah");
+        verify(stmt, times(0)).setObject(any(Integer.class), any(Object.class));
+        
+        executor.throwIfUnmappedParams();
+    }
+
+    @Test(expected=SQLException.class)
+    public void testMissingParamSql() throws SQLException {
+        createExecutor("select * from blah :first = first and :last=last");
+
+        verify(conn, times(1)).prepareStatement("select * from blah ? = first and ?=last");
+
+        executor.bind("first", "first_name")
+                .bind(":last", "last_name")
+                .bind("phone", Integer.valueOf(12345)); // should throw
+       
+        verify(stmt, times(1)).setObject(1, "first_name");
+        verify(stmt, times(1)).setObject(2, "last_name");
+        verify(stmt, times(1)).setObject(eq(3), eq(Integer.valueOf(12345)));
+    }
+
+    @Test(expected=SQLException.class)
+    public void testDoubleBind() throws SQLException {
+        createExecutor("select * from blah :first = first and :last=last");
+
+        verify(conn, times(1)).prepareStatement("select * from blah ? = first and ?=last");
+
+        executor.bind("first", "first_name")
+                .bind(":last", "last_name")
+                .bind(":last", "last_name");
+        
+        verify(stmt, times(1)).setObject(1, "first_name");
+        verify(stmt, times(1)).setObject(2, "last_name");
+    }
+}

Modified: commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/AsyncQueryRunnerTest.java
URL: http://svn.apache.org/viewvc/commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/AsyncQueryRunnerTest.java?rev=1450009&r1=1450008&r2=1450009&view=diff
==============================================================================
--- commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/AsyncQueryRunnerTest.java (original)
+++ commons/proper/dbutils/branches/2_0/src/test/java/org/apache/commons/dbutils/AsyncQueryRunnerTest.java Tue Feb 26 03:52:37 2013
@@ -16,18 +16,12 @@
  */
 package org.apache.commons.dbutils;
 
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import org.apache.commons.dbutils.handlers.ArrayHandler;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -36,226 +30,64 @@ import org.mockito.MockitoAnnotations;
 @SuppressWarnings("boxing") // test code
 public class AsyncQueryRunnerTest {
     AsyncQueryRunner runner;
-    ArrayHandler handler;
 
     @Mock QueryRunner qRunner;
-    @Mock Connection conn;
-
+    @Mock ResultSetHandler<Object> handler;
+    @Mock QueryExecutor queryExecutor;
+    @Mock UpdateExecutor updateExecutor;
+    @Mock InsertExecutor insertExecutor;
+    
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);    // init the mocks
 
-         handler = new ArrayHandler();
-         runner = new AsyncQueryRunner(Executors.newFixedThreadPool(1), qRunner);
-    }
-
-    //
-    // Batch test cases
-    //
-    private void callGoodBatch(Connection conn, Object[][] params) throws Exception {
-        Future<int[]> future = runner.batch(conn, "select * from blah where ? = ?", params);
-
-        future.get();
-
-        verify(qRunner, times(1)).batch(eq(conn), any(String.class), eq(params));
-    }
-
-    private void callGoodBatch(Object[][] params) throws Exception {
-        Future<int[]> future = runner.batch("select * from blah where ? = ?", params);
-
-        future.get();
-        
-        verify(qRunner, times(1)).batch(any(String.class), eq(params));
-    }
-
-    @Test
-    public void testGoodBatch() throws Exception {
-        String[][] params = new String[][] { { "unit", "unit" }, { "test", "test" } };
-
-        callGoodBatch(params);
-    }
-
-    @Test
-    public void testGoodBatchPmdTrue() throws Exception {
-        String[][] params = new String[][] { { "unit", "unit" }, { "test", "test" } };
-
-        callGoodBatch(params);
-    }
-
-    @Test
-    public void testGoodBatchDefaultConstructor() throws Exception {
-        String[][] params = new String[][] { { "unit", "unit" }, { "test", "test" } };
-
-        callGoodBatch(conn, params);
+         runner = new AsyncQueryRunner(Executors.newFixedThreadPool(1));
     }
 
     @Test
-    public void testNullParamsBatch() throws Exception {
-        String[][] params = new String[][] { { null, "unit" }, { "test", null } };
-
-        callGoodBatch(params);
-    }
-
-
-
-    // helper method for calling batch when an exception is expected
-    private void callBatchWithException(String sql, Object[][] params) throws Exception {
-        Future<int[]> future = null;
-        boolean caught = false;
-
-        try {
-            future = runner.batch(sql, params);
-            
-            future.get();
-
-            verify(qRunner, times(1)).batch(any(String.class), params);
-        } catch(Exception e) {
-            caught = true;
-        }
-
-        if(!caught)
-            fail("Exception never thrown, but expected");
-    }
-
-    @Test
-    public void testTooFewParamsBatch() throws Exception {
-        String[][] params = new String[][] { { "unit" }, { "test" } };
-
-        callBatchWithException("select * from blah where ? = ?", params);
-    }
-
-    @Test
-    public void testTooManyParamsBatch() throws Exception {
-        String[][] params = new String[][] { { "unit", "unit", "unit" }, { "test", "test", "test" } };
-
-        callBatchWithException("select * from blah where ? = ?", params);
+    public void testQueryExecutor() throws Exception {
+        runner.query(queryExecutor, handler).get();
+        
+        verify(queryExecutor, times(1)).query(handler);
     }
 
     @Test(expected=ExecutionException.class)
-    public void testExceptionBatch() throws Exception {
-        doThrow(ExecutionException.class).when(qRunner).batch(any(String.class), any(Object[][].class));
-        runner.batch("select * from blah where ? = ?", null).get();
-    }
-
-    @Test
-    public void testBatchException() throws Exception {
-        String[][] params = new String[][] { { "unit", "unit" }, { "test", "test" } };
-
-        doThrow(new SQLException()).when(qRunner).batch(any(String.class), any(Object[][].class));
-
-        callBatchWithException("select * from blah where ? = ?", params);
-    }
-
-
-    //
-    // Query test cases
-    //
-    private void callGoodQuery(Connection conn) throws Exception {
-        runner.query(conn, "select * from blah where ? = ?", handler, "unit", "test").get();
+    public void testQueryExecutorException() throws Exception {
+        doThrow(SQLException.class).when(queryExecutor).query(handler);
+        runner.query(queryExecutor, handler).get();
         
-        verify(qRunner, times(1)).query(eq(conn), any(String.class), eq(handler), any(String.class), any(String.class));
-
-        // call the other variation of query
-        runner.query(conn, "select * from blah", handler).get();
-
-        verify(qRunner, times(1)).query(eq(conn), any(String.class), eq(handler));
-    }
-
-    private void callGoodQuery() throws Exception {
-        runner.query("select * from blah where ? = ?", handler, "unit", "test").get();
-
-        verify(qRunner, times(1)).query(any(String.class), eq(handler), any(String.class), any(String.class));
-
-        // call the other variation of query
-        runner.query("select * from blah", handler).get();
-
-        verify(qRunner, times(1)).query(any(String.class), eq(handler));
+        verify(queryExecutor, times(1)).query(handler);
     }
 
     @Test
-    public void testGoodQuery() throws Exception {
-        callGoodQuery();
-    }
-
-    @Test
-    public void testGoodQueryWithConn() throws Exception {
-        callGoodQuery(conn);
+    public void testUpdateExecutor() throws Exception {
+        runner.update(updateExecutor).get();
+        
+        verify(updateExecutor, times(1)).update();
     }
 
     @Test(expected=ExecutionException.class)
-    public void testExecuteQueryException() throws Exception {
-        doThrow(new SQLException()).when(qRunner).query(any(String.class), eq(handler), any(String.class), any(String.class));
-
-        runner.query("select * from blah where ? = ?", handler, "unit", "test").get();
-    }
-
-
-    //
-    // Update test cases
-    //
-    private void callGoodUpdate(Connection conn) throws Exception {
-        runner.update(conn, "update blah set ? = ?", "unit", "test").get();
-
-        verify(qRunner, times(1)).update(eq(conn), any(String.class), any(String.class), any(String.class));
-
-        // call the other variation
-        runner.update(conn, "update blah set unit = test").get();
-
-        verify(qRunner, times(1)).update(eq(conn), any(String.class));
-
-        // call the other variation
-        runner.update(conn, "update blah set unit = ?", "test").get();
-
-        verify(qRunner, times(1)).update(eq(conn), any(String.class), any(String.class));
-    }
-
-    private void callGoodUpdate() throws Exception {
-        runner.update("update blah set ? = ?", "unit", "test").get();
-
-        verify(qRunner, times(1)).update(any(String.class), any(String.class), any(String.class));
-
-        // call the other variation
-        runner.update("update blah set unit = test").get();
-
-        verify(qRunner, times(1)).update(any(String.class));
-
-        // call the other variation
-        runner.update("update blah set unit = ?", "test").get();
-
-        verify(qRunner, times(1)).update(any(String.class), any(String.class));
-    }
-
-    @Test
-    public void testGoodUpdate() throws Exception {
-        callGoodUpdate();
-    }
-
-    @Test
-    public void testGoodUpdateConnection() throws Exception {
-        callGoodUpdate(conn);
+    public void testUpdateExecutorException() throws Exception {
+        doThrow(SQLException.class).when(updateExecutor).update();
+        runner.update(updateExecutor).get();
+        
+        verify(updateExecutor, times(1)).update();
     }
 
-    
     @Test
-    public void testInsertUsesGivenQueryRunner() throws Exception {
-    	runner.insert("1", handler);
-    	runner.insert("2", handler, "param1");
-    	runner.insert(conn, "3", handler);
-    	runner.insert(conn, "4", handler, "param1");
-    	
-    	verify(qRunner).insert("1", handler);
-    	verify(qRunner).insert("2", handler, "param1");
-    	verify(qRunner).insert(conn, "3", handler);
-    	verify(qRunner).insert(conn, "4", handler, "param1");
+    public void testInsertExecutor() throws Exception {
+        runner.insert(insertExecutor, handler).get();
+        
+        verify(insertExecutor, times(1)).insert(handler);
     }
 
     @Test(expected=ExecutionException.class)
-    public void testExceptionUpdate() throws Exception {
-        doThrow(new SQLException()).when(qRunner).update(any(String.class), any(String.class), any(String.class));
+    public void testInsertExecutorException() throws Exception {
+        doThrow(SQLException.class).when(insertExecutor).insert(handler);
+        runner.insert(insertExecutor, handler).get();
         
-        runner.update("select * from blah where ? = ?", "unit", "test").get();
-        
-        verify(qRunner, times(1)).update(any(String.class), any(String.class), any(String.class));
+        verify(insertExecutor, times(1)).insert(handler);
     }
 
+
 }