You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/08/30 15:14:02 UTC

[commons-dbutils] branch master updated: Use standard Javadoc @since tag format

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-dbutils.git


The following commit(s) were added to refs/heads/master by this push:
     new c442515  Use standard Javadoc @since tag format
c442515 is described below

commit c442515990f431cd33afdfedcb1bcc327967c1d0
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Tue Aug 30 11:13:58 2022 -0400

    Use standard Javadoc @since tag format
---
 .../commons/dbutils/AbstractQueryRunner.java       | 1270 +++++++++---------
 .../apache/commons/dbutils/AsyncQueryRunner.java   | 1382 ++++++++++----------
 .../apache/commons/dbutils/BasicRowProcessor.java  |    2 +-
 .../org/apache/commons/dbutils/BeanProcessor.java  |    2 +-
 .../java/org/apache/commons/dbutils/DbUtils.java   |  852 ++++++------
 .../org/apache/commons/dbutils/QueryLoader.java    |  284 ++--
 .../org/apache/commons/dbutils/QueryRunner.java    |    4 +-
 .../apache/commons/dbutils/ResultSetIterator.java  |  282 ++--
 .../dbutils/handlers/AbstractKeyedHandler.java     |  174 +--
 .../commons/dbutils/handlers/BeanMapHandler.java   |  368 +++---
 .../dbutils/handlers/ColumnListHandler.java        |  208 +--
 .../commons/dbutils/handlers/KeyedHandler.java     |  320 ++---
 12 files changed, 2574 insertions(+), 2574 deletions(-)

diff --git a/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java b/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java
index 8415173..c6492c9 100644
--- a/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java
+++ b/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java
@@ -1,635 +1,635 @@
-/*
- * 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.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.sql.CallableStatement;
-import java.sql.Connection;
-import java.sql.ParameterMetaData;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.SQLFeatureNotSupportedException;
-import java.sql.Statement;
-import java.sql.Types;
-import java.util.Arrays;
-
-import javax.sql.DataSource;
-
-/**
- * The base class for QueryRunner &amp; 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.
-     * @deprecated Access to this field should be through {@link #getDataSource()}.
-     */
-    @Deprecated
-    protected final DataSource ds;
-
-    /**
-     * Configuration to use when preparing statements.
-     */
-    private final StatementConfiguration stmtConfig;
-
-    /**
-     * Default constructor, sets pmdKnownBroken to false, ds to null and stmtConfig to null.
-     */
-    public AbstractQueryRunner() {
-        ds = null;
-        this.stmtConfig = null;
-    }
-
-    /**
-     * Constructor to control the use of {@code ParameterMetaData}.
-     *
-     * @param pmdKnownBroken
-     *            Some drivers don't support
-     *            {@link ParameterMetaData#getParameterType(int) }; if
-     *            {@code pmdKnownBroken} 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(final boolean pmdKnownBroken) {
-        this.pmdKnownBroken = pmdKnownBroken;
-        ds = null;
-        this.stmtConfig = null;
-    }
-
-    /**
-     * Constructor to provide a {@code DataSource}. Methods that do not
-     * take a {@code Connection} parameter will retrieve connections from
-     * this {@code DataSource}.
-     *
-     * @param ds
-     *            The {@code DataSource} to retrieve connections from.
-     */
-    public AbstractQueryRunner(final DataSource ds) {
-        this.ds = ds;
-        this.stmtConfig = null;
-    }
-
-    /**
-     * Constructor to provide a {@code DataSource} and control the use of
-     * {@code ParameterMetaData}. Methods that do not take a
-     * {@code Connection} parameter will retrieve connections from this
-     * {@code DataSource}.
-     *
-     * @param ds
-     *            The {@code DataSource} to retrieve connections from.
-     * @param pmdKnownBroken
-     *            Some drivers don't support
-     *            {@link ParameterMetaData#getParameterType(int) }; if
-     *            {@code pmdKnownBroken} 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(final DataSource ds, final boolean pmdKnownBroken) {
-        this.pmdKnownBroken = pmdKnownBroken;
-        this.ds = ds;
-        this.stmtConfig = null;
-    }
-
-    /**
-     * Constructor for QueryRunner that takes a {@code DataSource}, a {@code StatementConfiguration}, and
-     * controls the use of {@code ParameterMetaData}.  Methods that do not take a {@code Connection} parameter
-     * will retrieve connections from this {@code DataSource}.
-     *
-     * @param ds The {@code DataSource} to retrieve connections from.
-     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
-     * if {@code pmdKnownBroken} 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.
-     * @param stmtConfig The configuration to apply to statements when they are prepared.
-     */
-    public AbstractQueryRunner(final DataSource ds, final boolean pmdKnownBroken, final StatementConfiguration stmtConfig) {
-        this.pmdKnownBroken = pmdKnownBroken;
-        this.ds = ds;
-        this.stmtConfig = stmtConfig;
-    }
-
-    /**
-     * Constructor for QueryRunner that takes a {@code DataSource} to use and a {@code StatementConfiguration}.
-     *
-     * Methods that do not take a {@code Connection} parameter will retrieve connections from this
-     * {@code DataSource}.
-     *
-     * @param ds The {@code DataSource} to retrieve connections from.
-     * @param stmtConfig The configuration to apply to statements when they are prepared.
-     */
-    public AbstractQueryRunner(final DataSource ds, final StatementConfiguration stmtConfig) {
-        this.ds = ds;
-        this.stmtConfig = stmtConfig;
-    }
-
-    /**
-     * Constructor for QueryRunner that takes a {@code StatementConfiguration} to configure statements when
-     * preparing them.
-     *
-     * @param stmtConfig The configuration to apply to statements when they are prepared.
-     */
-    public AbstractQueryRunner(final StatementConfiguration stmtConfig) {
-        this.ds = null;
-        this.stmtConfig = stmtConfig;
-    }
-
-    /**
-     * Close a {@code Connection}. 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
-     */
-    protected void close(final Connection conn) throws SQLException {
-        DbUtils.close(conn);
-    }
-
-    /**
-     * Close a {@code ResultSet}. 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(final ResultSet rs) throws SQLException {
-        DbUtils.close(rs);
-    }
-
-    /**
-     * Close a {@code Statement}. 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(final Statement stmt) throws SQLException {
-        DbUtils.close(stmt);
-    }
-
-    /**
-     * Calls {@link DbUtils#closeQuietly(Connection)}.
-     *
-     * @param conn Connection to close.
-     * @since 2.0
-     */
-    protected void closeQuietly(final Connection conn) {
-        DbUtils.closeQuietly(conn);
-    }
-
-    /**
-     * Calls {@link DbUtils#closeQuietly(ResultSet)}.
-     *
-     * @param rs ResultSet to close.
-     * @since 2.0
-     */
-    protected void closeQuietly(final ResultSet rs) {
-        DbUtils.closeQuietly(rs);
-    }
-
-    /**
-     * Calls {@link DbUtils#closeQuietly(Statement)}.
-     *
-     * @param statement ResultSet to close.
-     * @since 2.0
-     */
-    protected void closeQuietly(final Statement statement) {
-        DbUtils.closeQuietly(statement);
-    }
-
-    private void configureStatement(final Statement stmt) throws SQLException {
-
-        if (stmtConfig != null) {
-            if (stmtConfig.isFetchDirectionSet()) {
-                stmt.setFetchDirection(stmtConfig.getFetchDirection());
-            }
-
-            if (stmtConfig.isFetchSizeSet()) {
-                stmt.setFetchSize(stmtConfig.getFetchSize());
-            }
-
-            if (stmtConfig.isMaxFieldSizeSet()) {
-                stmt.setMaxFieldSize(stmtConfig.getMaxFieldSize());
-            }
-
-            if (stmtConfig.isMaxRowsSet()) {
-                stmt.setMaxRows(stmtConfig.getMaxRows());
-            }
-
-            if (stmtConfig.isQueryTimeoutSet()) {
-                stmt.setQueryTimeout(stmtConfig.getQueryTimeout());
-            }
-        }
-    }
-
-    /**
-     * Fill the {@code PreparedStatement} replacement parameters with the
-     * given objects.
-     *
-     * @param stmt
-     *            PreparedStatement to fill
-     * @param params
-     *            Query replacement parameters; {@code null} is a valid
-     *            value to pass in.
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    public void fillStatement(final PreparedStatement stmt, final Object... params)
-            throws SQLException {
-
-        // check the parameter count, if we can
-        ParameterMetaData pmd = null;
-        if (!pmdKnownBroken) {
-            try {
-                pmd = stmt.getParameterMetaData();
-                if (pmd == null) { // can be returned by implementations that don't support the method
-                    pmdKnownBroken = true;
-                } else {
-                    final int stmtCount = pmd.getParameterCount();
-                    final int paramsCount = params == null ? 0 : params.length;
-
-                    if (stmtCount != paramsCount) {
-                        throw new SQLException("Wrong number of parameters: expected "
-                                + stmtCount + ", was given " + paramsCount);
-                    }
-                }
-            } catch (final SQLFeatureNotSupportedException ex) {
-                pmdKnownBroken = true;
-            }
-            // TODO see DBUTILS-117: would it make sense to catch any other SQLEx types here?
-        }
-
-        // nothing to do here
-        if (params == null) {
-            return;
-        }
-
-        CallableStatement call = null;
-        if (stmt instanceof CallableStatement) {
-            call = (CallableStatement) stmt;
-        }
-
-        for (int i = 0; i < params.length; i++) {
-            if (params[i] != null) {
-                if (call != null && params[i] instanceof OutParameter) {
-                    ((OutParameter)params[i]).register(call, i + 1);
-                } else {
-                    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) {
-                    // TODO see DBUTILS-117: does it make sense to catch SQLEx here?
-                    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 (final SQLException e) {
-                        pmdKnownBroken = true;
-                    }
-                }
-                stmt.setNull(i + 1, sqlType);
-            }
-        }
-    }
-
-    /**
-     * Fill the {@code PreparedStatement} 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(final PreparedStatement stmt, final Object bean,
-            final PropertyDescriptor[] properties) throws SQLException {
-        final Object[] params = new Object[properties.length];
-        for (int i = 0; i < properties.length; i++) {
-            final PropertyDescriptor property = properties[i];
-            Object value = null;
-            final Method method = property.getReadMethod();
-            if (method == null) {
-                throw new RuntimeException("No read method for bean property "
-                        + bean.getClass() + " " + property.getName());
-            }
-            try {
-                value = method.invoke(bean);
-            } catch (final IllegalArgumentException e) {
-                throw new RuntimeException(
-                        "Couldn't invoke method with 0 arguments: " + method, e);
-            } catch (final InvocationTargetException | IllegalAccessException e) {
-                throw new RuntimeException("Couldn't invoke method: " + method,
-                        e);
-            }
-            params[i] = value;
-        }
-        fillStatement(stmt, params);
-    }
-
-    /**
-     * Fill the {@code PreparedStatement} replacement parameters with the
-     * given object's bean property values.
-     *
-     * @param stmt
-     *            PreparedStatement to fill
-     * @param bean
-     *            A JavaBean object
-     * @param propertyNames
-     *            An ordered array of property names (these should match the
-     *            getters/setters); this gives the order to insert values in the
-     *            statement
-     * @throws SQLException
-     *             If a database access error occurs
-     */
-    public void fillStatementWithBean(final PreparedStatement stmt, final Object bean,
-            final String... propertyNames) throws SQLException {
-        PropertyDescriptor[] descriptors;
-        try {
-            descriptors = Introspector.getBeanInfo(bean.getClass())
-                    .getPropertyDescriptors();
-        } catch (final IntrospectionException e) {
-            throw new RuntimeException("Couldn't introspect bean "
-                    + bean.getClass().toString(), e);
-        }
-        final PropertyDescriptor[] sorted = new PropertyDescriptor[propertyNames.length];
-        for (int i = 0; i < propertyNames.length; i++) {
-            final String propertyName = propertyNames[i];
-            if (propertyName == null) {
-                throw new NullPointerException("propertyName can't be null: "
-                        + i);
-            }
-            boolean found = false;
-            for (final PropertyDescriptor descriptor : descriptors) {
-                if (propertyName.equals(descriptor.getName())) {
-                    sorted[i] = descriptor;
-                    found = true;
-                    break;
-                }
-            }
-            if (!found) {
-                throw new RuntimeException("Couldn't find bean property: "
-                        + bean.getClass() + " " + propertyName);
-            }
-        }
-        fillStatementWithBean(stmt, bean, sorted);
-    }
-
-    /**
-     * Returns the {@code DataSource} this runner is using.
-     * {@code QueryRunner} methods always call this method to get the
-     * {@code DataSource} so subclasses can provide specialized behavior.
-     *
-     * @return DataSource the runner is using
-     */
-    public DataSource getDataSource() {
-        return this.ds;
-    }
-
-    /**
-     * Some drivers don't support
-     * {@link ParameterMetaData#getParameterType(int) }; if
-     * {@code pmdKnownBroken} 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 CallableStatement} object for the given SQL.
-     * {@code QueryRunner} methods always call this method to prepare
-     * callable statements for them. Subclasses can override this method to
-     * provide special CallableStatement configuration if needed. This
-     * implementation simply calls {@code conn.prepareCall(sql)}.
-     *
-     * @param conn
-     *            The {@code Connection} used to create the
-     *            {@code CallableStatement}
-     * @param sql
-     *            The SQL statement to prepare.
-     * @return An initialized {@code CallableStatement}.
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    protected CallableStatement prepareCall(final Connection conn, final String sql)
-            throws SQLException {
-
-        return conn.prepareCall(sql);
-    }
-
-    /**
-     * Factory method that creates and initializes a {@code Connection}
-     * object. {@code QueryRunner} methods always call this method to
-     * retrieve connections from its DataSource. Subclasses can override this
-     * method to provide special {@code Connection} configuration if
-     * needed. This implementation simply calls {@code ds.getConnection()}.
-     *
-     * @return An initialized {@code Connection}.
-     * @throws SQLException
-     *             if a database access error occurs
-     * @since DbUtils 1.1
-     */
-    protected Connection prepareConnection() throws SQLException {
-        if (this.getDataSource() == null) {
-            throw new SQLException(
-                    "QueryRunner requires a DataSource to be "
-                            + "invoked in this way, or a Connection should be passed in");
-        }
-        return this.getDataSource().getConnection();
-    }
-
-    /**
-     * Factory method that creates and initializes a
-     * {@code PreparedStatement} object for the given SQL.
-     * {@code QueryRunner} 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)}.
-     *
-     * @param conn
-     *            The {@code Connection} used to create the
-     *            {@code PreparedStatement}
-     * @param sql
-     *            The SQL statement to prepare.
-     * @return An initialized {@code PreparedStatement}.
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    protected PreparedStatement prepareStatement(final Connection conn, final String sql)
-            throws SQLException {
-
-        @SuppressWarnings("resource")
-        final
-        PreparedStatement ps = conn.prepareStatement(sql);
-        try {
-            configureStatement(ps);
-        } catch (final SQLException e) {
-            ps.close();
-            throw e;
-        }
-        return ps;
-    }
-
-    /**
-     * Factory method that creates and initializes a
-     * {@code PreparedStatement} object for the given SQL.
-     * {@code QueryRunner} 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)}
-     * which will result in the ability to retrieve the automatically-generated
-     * keys from an auto_increment column.
-     *
-     * @param conn
-     *            The {@code Connection} used to create the
-     *            {@code PreparedStatement}
-     * @param sql
-     *            The SQL statement to prepare.
-     * @param returnedKeys
-     *            Flag indicating whether to return generated keys or not.
-     *
-     * @return An initialized {@code PreparedStatement}.
-     * @throws SQLException
-     *             if a database access error occurs
-     * @since 1.6
-     */
-    protected PreparedStatement prepareStatement(final Connection conn, final String sql, final int returnedKeys)
-            throws SQLException {
-
-        @SuppressWarnings("resource")
-        final
-        PreparedStatement ps = conn.prepareStatement(sql, returnedKeys);
-        try {
-            configureStatement(ps);
-        } catch (final SQLException e) {
-            ps.close();
-            throw e;
-        }
-        return ps;
-    }
-
-    /**
-     * 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} is a valid
-     *            value to pass in.
-     *
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    protected void rethrow(final SQLException cause, final String sql, final Object... params)
-            throws SQLException {
-
-        String causeMessage = cause.getMessage();
-        if (causeMessage == null) {
-            causeMessage = "";
-        }
-        final StringBuilder msg = new StringBuilder(causeMessage);
-
-        msg.append(" Query: ");
-        msg.append(sql);
-        msg.append(" Parameters: ");
-
-        if (params == null) {
-            msg.append("[]");
-        } else {
-            msg.append(Arrays.deepToString(params));
-        }
-
-        final SQLException e = new SQLException(msg.toString(), cause.getSQLState(),
-                cause.getErrorCode());
-        e.setNextException(cause);
-
-        throw e;
-    }
-
-    /**
-     * Wrap the {@code ResultSet} in a decorator before processing it. This
-     * implementation returns the {@code ResultSet} 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} to decorate; never
-     *            {@code null}.
-     * @return The {@code ResultSet} wrapped in some decorator.
-     */
-    protected ResultSet wrap(final ResultSet rs) {
-        return rs;
-    }
-
-}
+/*
+ * 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.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.ParameterMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.Arrays;
+
+import javax.sql.DataSource;
+
+/**
+ * The base class for QueryRunner &amp; 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.
+     * @deprecated Access to this field should be through {@link #getDataSource()}.
+     */
+    @Deprecated
+    protected final DataSource ds;
+
+    /**
+     * Configuration to use when preparing statements.
+     */
+    private final StatementConfiguration stmtConfig;
+
+    /**
+     * Default constructor, sets pmdKnownBroken to false, ds to null and stmtConfig to null.
+     */
+    public AbstractQueryRunner() {
+        ds = null;
+        this.stmtConfig = null;
+    }
+
+    /**
+     * Constructor to control the use of {@code ParameterMetaData}.
+     *
+     * @param pmdKnownBroken
+     *            Some drivers don't support
+     *            {@link ParameterMetaData#getParameterType(int) }; if
+     *            {@code pmdKnownBroken} 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(final boolean pmdKnownBroken) {
+        this.pmdKnownBroken = pmdKnownBroken;
+        ds = null;
+        this.stmtConfig = null;
+    }
+
+    /**
+     * Constructor to provide a {@code DataSource}. Methods that do not
+     * take a {@code Connection} parameter will retrieve connections from
+     * this {@code DataSource}.
+     *
+     * @param ds
+     *            The {@code DataSource} to retrieve connections from.
+     */
+    public AbstractQueryRunner(final DataSource ds) {
+        this.ds = ds;
+        this.stmtConfig = null;
+    }
+
+    /**
+     * Constructor to provide a {@code DataSource} and control the use of
+     * {@code ParameterMetaData}. Methods that do not take a
+     * {@code Connection} parameter will retrieve connections from this
+     * {@code DataSource}.
+     *
+     * @param ds
+     *            The {@code DataSource} to retrieve connections from.
+     * @param pmdKnownBroken
+     *            Some drivers don't support
+     *            {@link ParameterMetaData#getParameterType(int) }; if
+     *            {@code pmdKnownBroken} 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(final DataSource ds, final boolean pmdKnownBroken) {
+        this.pmdKnownBroken = pmdKnownBroken;
+        this.ds = ds;
+        this.stmtConfig = null;
+    }
+
+    /**
+     * Constructor for QueryRunner that takes a {@code DataSource}, a {@code StatementConfiguration}, and
+     * controls the use of {@code ParameterMetaData}.  Methods that do not take a {@code Connection} parameter
+     * will retrieve connections from this {@code DataSource}.
+     *
+     * @param ds The {@code DataSource} to retrieve connections from.
+     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
+     * if {@code pmdKnownBroken} 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.
+     * @param stmtConfig The configuration to apply to statements when they are prepared.
+     */
+    public AbstractQueryRunner(final DataSource ds, final boolean pmdKnownBroken, final StatementConfiguration stmtConfig) {
+        this.pmdKnownBroken = pmdKnownBroken;
+        this.ds = ds;
+        this.stmtConfig = stmtConfig;
+    }
+
+    /**
+     * Constructor for QueryRunner that takes a {@code DataSource} to use and a {@code StatementConfiguration}.
+     *
+     * Methods that do not take a {@code Connection} parameter will retrieve connections from this
+     * {@code DataSource}.
+     *
+     * @param ds The {@code DataSource} to retrieve connections from.
+     * @param stmtConfig The configuration to apply to statements when they are prepared.
+     */
+    public AbstractQueryRunner(final DataSource ds, final StatementConfiguration stmtConfig) {
+        this.ds = ds;
+        this.stmtConfig = stmtConfig;
+    }
+
+    /**
+     * Constructor for QueryRunner that takes a {@code StatementConfiguration} to configure statements when
+     * preparing them.
+     *
+     * @param stmtConfig The configuration to apply to statements when they are prepared.
+     */
+    public AbstractQueryRunner(final StatementConfiguration stmtConfig) {
+        this.ds = null;
+        this.stmtConfig = stmtConfig;
+    }
+
+    /**
+     * Close a {@code Connection}. 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 1.1
+     */
+    protected void close(final Connection conn) throws SQLException {
+        DbUtils.close(conn);
+    }
+
+    /**
+     * Close a {@code ResultSet}. 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 1.1
+     */
+    protected void close(final ResultSet rs) throws SQLException {
+        DbUtils.close(rs);
+    }
+
+    /**
+     * Close a {@code Statement}. 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 1.1
+     */
+    protected void close(final Statement stmt) throws SQLException {
+        DbUtils.close(stmt);
+    }
+
+    /**
+     * Calls {@link DbUtils#closeQuietly(Connection)}.
+     *
+     * @param conn Connection to close.
+     * @since 2.0
+     */
+    protected void closeQuietly(final Connection conn) {
+        DbUtils.closeQuietly(conn);
+    }
+
+    /**
+     * Calls {@link DbUtils#closeQuietly(ResultSet)}.
+     *
+     * @param rs ResultSet to close.
+     * @since 2.0
+     */
+    protected void closeQuietly(final ResultSet rs) {
+        DbUtils.closeQuietly(rs);
+    }
+
+    /**
+     * Calls {@link DbUtils#closeQuietly(Statement)}.
+     *
+     * @param statement ResultSet to close.
+     * @since 2.0
+     */
+    protected void closeQuietly(final Statement statement) {
+        DbUtils.closeQuietly(statement);
+    }
+
+    private void configureStatement(final Statement stmt) throws SQLException {
+
+        if (stmtConfig != null) {
+            if (stmtConfig.isFetchDirectionSet()) {
+                stmt.setFetchDirection(stmtConfig.getFetchDirection());
+            }
+
+            if (stmtConfig.isFetchSizeSet()) {
+                stmt.setFetchSize(stmtConfig.getFetchSize());
+            }
+
+            if (stmtConfig.isMaxFieldSizeSet()) {
+                stmt.setMaxFieldSize(stmtConfig.getMaxFieldSize());
+            }
+
+            if (stmtConfig.isMaxRowsSet()) {
+                stmt.setMaxRows(stmtConfig.getMaxRows());
+            }
+
+            if (stmtConfig.isQueryTimeoutSet()) {
+                stmt.setQueryTimeout(stmtConfig.getQueryTimeout());
+            }
+        }
+    }
+
+    /**
+     * Fill the {@code PreparedStatement} replacement parameters with the
+     * given objects.
+     *
+     * @param stmt
+     *            PreparedStatement to fill
+     * @param params
+     *            Query replacement parameters; {@code null} is a valid
+     *            value to pass in.
+     * @throws SQLException
+     *             if a database access error occurs
+     */
+    public void fillStatement(final PreparedStatement stmt, final Object... params)
+            throws SQLException {
+
+        // check the parameter count, if we can
+        ParameterMetaData pmd = null;
+        if (!pmdKnownBroken) {
+            try {
+                pmd = stmt.getParameterMetaData();
+                if (pmd == null) { // can be returned by implementations that don't support the method
+                    pmdKnownBroken = true;
+                } else {
+                    final int stmtCount = pmd.getParameterCount();
+                    final int paramsCount = params == null ? 0 : params.length;
+
+                    if (stmtCount != paramsCount) {
+                        throw new SQLException("Wrong number of parameters: expected "
+                                + stmtCount + ", was given " + paramsCount);
+                    }
+                }
+            } catch (final SQLFeatureNotSupportedException ex) {
+                pmdKnownBroken = true;
+            }
+            // TODO see DBUTILS-117: would it make sense to catch any other SQLEx types here?
+        }
+
+        // nothing to do here
+        if (params == null) {
+            return;
+        }
+
+        CallableStatement call = null;
+        if (stmt instanceof CallableStatement) {
+            call = (CallableStatement) stmt;
+        }
+
+        for (int i = 0; i < params.length; i++) {
+            if (params[i] != null) {
+                if (call != null && params[i] instanceof OutParameter) {
+                    ((OutParameter)params[i]).register(call, i + 1);
+                } else {
+                    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) {
+                    // TODO see DBUTILS-117: does it make sense to catch SQLEx here?
+                    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 (final SQLException e) {
+                        pmdKnownBroken = true;
+                    }
+                }
+                stmt.setNull(i + 1, sqlType);
+            }
+        }
+    }
+
+    /**
+     * Fill the {@code PreparedStatement} 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(final PreparedStatement stmt, final Object bean,
+            final PropertyDescriptor[] properties) throws SQLException {
+        final Object[] params = new Object[properties.length];
+        for (int i = 0; i < properties.length; i++) {
+            final PropertyDescriptor property = properties[i];
+            Object value = null;
+            final Method method = property.getReadMethod();
+            if (method == null) {
+                throw new RuntimeException("No read method for bean property "
+                        + bean.getClass() + " " + property.getName());
+            }
+            try {
+                value = method.invoke(bean);
+            } catch (final IllegalArgumentException e) {
+                throw new RuntimeException(
+                        "Couldn't invoke method with 0 arguments: " + method, e);
+            } catch (final InvocationTargetException | IllegalAccessException e) {
+                throw new RuntimeException("Couldn't invoke method: " + method,
+                        e);
+            }
+            params[i] = value;
+        }
+        fillStatement(stmt, params);
+    }
+
+    /**
+     * Fill the {@code PreparedStatement} replacement parameters with the
+     * given object's bean property values.
+     *
+     * @param stmt
+     *            PreparedStatement to fill
+     * @param bean
+     *            A JavaBean object
+     * @param propertyNames
+     *            An ordered array of property names (these should match the
+     *            getters/setters); this gives the order to insert values in the
+     *            statement
+     * @throws SQLException
+     *             If a database access error occurs
+     */
+    public void fillStatementWithBean(final PreparedStatement stmt, final Object bean,
+            final String... propertyNames) throws SQLException {
+        PropertyDescriptor[] descriptors;
+        try {
+            descriptors = Introspector.getBeanInfo(bean.getClass())
+                    .getPropertyDescriptors();
+        } catch (final IntrospectionException e) {
+            throw new RuntimeException("Couldn't introspect bean "
+                    + bean.getClass().toString(), e);
+        }
+        final PropertyDescriptor[] sorted = new PropertyDescriptor[propertyNames.length];
+        for (int i = 0; i < propertyNames.length; i++) {
+            final String propertyName = propertyNames[i];
+            if (propertyName == null) {
+                throw new NullPointerException("propertyName can't be null: "
+                        + i);
+            }
+            boolean found = false;
+            for (final PropertyDescriptor descriptor : descriptors) {
+                if (propertyName.equals(descriptor.getName())) {
+                    sorted[i] = descriptor;
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                throw new RuntimeException("Couldn't find bean property: "
+                        + bean.getClass() + " " + propertyName);
+            }
+        }
+        fillStatementWithBean(stmt, bean, sorted);
+    }
+
+    /**
+     * Returns the {@code DataSource} this runner is using.
+     * {@code QueryRunner} methods always call this method to get the
+     * {@code DataSource} so subclasses can provide specialized behavior.
+     *
+     * @return DataSource the runner is using
+     */
+    public DataSource getDataSource() {
+        return this.ds;
+    }
+
+    /**
+     * Some drivers don't support
+     * {@link ParameterMetaData#getParameterType(int) }; if
+     * {@code pmdKnownBroken} 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 CallableStatement} object for the given SQL.
+     * {@code QueryRunner} methods always call this method to prepare
+     * callable statements for them. Subclasses can override this method to
+     * provide special CallableStatement configuration if needed. This
+     * implementation simply calls {@code conn.prepareCall(sql)}.
+     *
+     * @param conn
+     *            The {@code Connection} used to create the
+     *            {@code CallableStatement}
+     * @param sql
+     *            The SQL statement to prepare.
+     * @return An initialized {@code CallableStatement}.
+     * @throws SQLException
+     *             if a database access error occurs
+     */
+    protected CallableStatement prepareCall(final Connection conn, final String sql)
+            throws SQLException {
+
+        return conn.prepareCall(sql);
+    }
+
+    /**
+     * Factory method that creates and initializes a {@code Connection}
+     * object. {@code QueryRunner} methods always call this method to
+     * retrieve connections from its DataSource. Subclasses can override this
+     * method to provide special {@code Connection} configuration if
+     * needed. This implementation simply calls {@code ds.getConnection()}.
+     *
+     * @return An initialized {@code Connection}.
+     * @throws SQLException
+     *             if a database access error occurs
+     * @since 1.1
+     */
+    protected Connection prepareConnection() throws SQLException {
+        if (this.getDataSource() == null) {
+            throw new SQLException(
+                    "QueryRunner requires a DataSource to be "
+                            + "invoked in this way, or a Connection should be passed in");
+        }
+        return this.getDataSource().getConnection();
+    }
+
+    /**
+     * Factory method that creates and initializes a
+     * {@code PreparedStatement} object for the given SQL.
+     * {@code QueryRunner} 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)}.
+     *
+     * @param conn
+     *            The {@code Connection} used to create the
+     *            {@code PreparedStatement}
+     * @param sql
+     *            The SQL statement to prepare.
+     * @return An initialized {@code PreparedStatement}.
+     * @throws SQLException
+     *             if a database access error occurs
+     */
+    protected PreparedStatement prepareStatement(final Connection conn, final String sql)
+            throws SQLException {
+
+        @SuppressWarnings("resource")
+        final
+        PreparedStatement ps = conn.prepareStatement(sql);
+        try {
+            configureStatement(ps);
+        } catch (final SQLException e) {
+            ps.close();
+            throw e;
+        }
+        return ps;
+    }
+
+    /**
+     * Factory method that creates and initializes a
+     * {@code PreparedStatement} object for the given SQL.
+     * {@code QueryRunner} 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)}
+     * which will result in the ability to retrieve the automatically-generated
+     * keys from an auto_increment column.
+     *
+     * @param conn
+     *            The {@code Connection} used to create the
+     *            {@code PreparedStatement}
+     * @param sql
+     *            The SQL statement to prepare.
+     * @param returnedKeys
+     *            Flag indicating whether to return generated keys or not.
+     *
+     * @return An initialized {@code PreparedStatement}.
+     * @throws SQLException
+     *             if a database access error occurs
+     * @since 1.6
+     */
+    protected PreparedStatement prepareStatement(final Connection conn, final String sql, final int returnedKeys)
+            throws SQLException {
+
+        @SuppressWarnings("resource")
+        final
+        PreparedStatement ps = conn.prepareStatement(sql, returnedKeys);
+        try {
+            configureStatement(ps);
+        } catch (final SQLException e) {
+            ps.close();
+            throw e;
+        }
+        return ps;
+    }
+
+    /**
+     * 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} is a valid
+     *            value to pass in.
+     *
+     * @throws SQLException
+     *             if a database access error occurs
+     */
+    protected void rethrow(final SQLException cause, final String sql, final Object... params)
+            throws SQLException {
+
+        String causeMessage = cause.getMessage();
+        if (causeMessage == null) {
+            causeMessage = "";
+        }
+        final StringBuilder msg = new StringBuilder(causeMessage);
+
+        msg.append(" Query: ");
+        msg.append(sql);
+        msg.append(" Parameters: ");
+
+        if (params == null) {
+            msg.append("[]");
+        } else {
+            msg.append(Arrays.deepToString(params));
+        }
+
+        final SQLException e = new SQLException(msg.toString(), cause.getSQLState(),
+                cause.getErrorCode());
+        e.setNextException(cause);
+
+        throw e;
+    }
+
+    /**
+     * Wrap the {@code ResultSet} in a decorator before processing it. This
+     * implementation returns the {@code ResultSet} 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} to decorate; never
+     *            {@code null}.
+     * @return The {@code ResultSet} wrapped in some decorator.
+     */
+    protected ResultSet wrap(final ResultSet rs) {
+        return rs;
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.java b/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.java
index f430a28..c9aaa67 100644
--- a/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.java
+++ b/src/main/java/org/apache/commons/dbutils/AsyncQueryRunner.java
@@ -1,691 +1,691 @@
-/*
- * 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.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
-import javax.sql.DataSource;
-
-/**
- * Executes SQL queries with pluggable strategies for handling
- * {@code ResultSet}s.  This class is thread safe.
- *
- * @see ResultSetHandler
- * @since 1.4
- */
-public class AsyncQueryRunner extends AbstractQueryRunner {
-
-    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(final ExecutorService executorService, final QueryRunner queryRunner) {
-        this.executorService = executorService;
-        this.queryRunner = queryRunner;
-    }
-
-    /**
-     * Constructor for AsyncQueryRunner.
-     *
-     * @param executorService the {@code ExecutorService} instance used to run JDBC invocations concurrently.
-     */
-    public AsyncQueryRunner(final ExecutorService executorService) {
-        this(null, false, executorService);
-    }
-
-    /**
-     * @deprecated Use {@link #AsyncQueryRunner(ExecutorService, QueryRunner)} instead.
-     * Constructor for AsyncQueryRunner that controls the use of {@code ParameterMetaData}.
-     *
-     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
-     * if {@code pmdKnownBroken} 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.
-     * @param executorService the {@code ExecutorService} instance used to run JDBC invocations concurrently.
-     */
-    @Deprecated
-    public AsyncQueryRunner(final boolean pmdKnownBroken, final ExecutorService executorService) {
-        this(null, pmdKnownBroken, executorService);
-    }
-
-    /**
-     * @deprecated Use {@link #AsyncQueryRunner(ExecutorService, QueryRunner)} instead.
-     * Constructor for AsyncQueryRunner that takes a {@code DataSource}.
-     *
-     * Methods that do not take a {@code Connection} parameter will retrieve connections from this
-     * {@code DataSource}.
-     *
-     * @param ds The {@code DataSource} to retrieve connections from.
-     * @param executorService the {@code ExecutorService} instance used to run JDBC invocations concurrently.
-     */
-    @Deprecated
-    public AsyncQueryRunner(final DataSource ds, final ExecutorService executorService) {
-        this(ds, false, executorService);
-    }
-
-    /**
-     * @deprecated Use {@link #AsyncQueryRunner(ExecutorService, QueryRunner)} instead.
-     * Constructor for AsyncQueryRunner that take a {@code DataSource} and controls the use of {@code ParameterMetaData}.
-     * Methods that do not take a {@code Connection} parameter will retrieve connections from this
-     * {@code DataSource}.
-     *
-     * @param ds The {@code DataSource} to retrieve connections from.
-     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
-     * if {@code pmdKnownBroken} 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.
-     * @param executorService the {@code ExecutorService} instance used to run JDBC invocations concurrently.
-     */
-    @Deprecated
-    public AsyncQueryRunner(final DataSource ds, final boolean pmdKnownBroken, final ExecutorService executorService) {
-        super(ds, pmdKnownBroken);
-        this.executorService = executorService;
-        this.queryRunner = new QueryRunner(ds, pmdKnownBroken);
-    }
-
-    /**
-     * @deprecated No longer used by this class. Will be removed in a future version.
-     * Class that encapsulates the continuation for batch calls.
-     */
-    @Deprecated
-    protected class BatchCallableStatement implements Callable<int[]> {
-        private final String sql;
-        private final Object[][] params;
-        private final Connection conn;
-        private final boolean closeConn;
-        private final PreparedStatement ps;
-
-        /**
-         * Creates a new BatchCallableStatement instance.
-         *
-         * @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.
-         * @param conn The connection to use for the batch call.
-         * @param closeConn True if the connection should be closed, false otherwise.
-         * @param ps The {@link PreparedStatement} to be executed.
-         */
-        public BatchCallableStatement(final String sql, final Object[][] params, final Connection conn, final boolean closeConn, final PreparedStatement ps) {
-            this.sql = sql;
-            this.params = params.clone();
-            this.conn = conn;
-            this.closeConn = closeConn;
-            this.ps = ps;
-        }
-
-        /**
-         * The actual call to executeBatch.
-         *
-         * @return an array of update counts containing one element for each command in the batch.
-         * @throws SQLException if a database access error occurs or one of the commands sent to the database fails.
-         * @see PreparedStatement#executeBatch()
-         */
-        @Override
-        public int[] call() throws SQLException {
-            int[] ret = null;
-
-            try {
-                ret = ps.executeBatch();
-            } catch (final SQLException e) {
-                rethrow(e, sql, (Object[])params);
-            } finally {
-                close(ps);
-                if (closeConn) {
-                    close(conn);
-                }
-            }
-
-            return ret;
-        }
-    }
-
-    /**
-     * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
-     *
-     * @param conn The {@code Connection} to use to run the query.  The caller is
-     * responsible for closing this Connection.
-     * @param sql The SQL to execute.
-     * @param params An array of query replacement parameters.  Each row in
-     * this array is one set of batch replacement values.
-     * @return A {@code Future} 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} is retrieved from the {@code DataSource}
-     * set in the constructor.  This {@code Connection} 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} 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);
-            }
-
-        });
-    }
-
-    /**
-     * Class that encapsulates the continuation for query calls.
-     * @param <T> The type of the result from the call to handle.
-     */
-    protected class QueryCallableStatement<T> implements Callable<T> {
-        private final String sql;
-        private final Object[] params;
-        private final Connection conn;
-        private final boolean closeConn;
-        private final PreparedStatement ps;
-        private final ResultSetHandler<T> rsh;
-
-        /**
-         * Creates a new {@code QueryCallableStatement} instance.
-         *
-         * @param conn The connection to use for the batch call.
-         * @param closeConn True if the connection should be closed, false otherwise.
-         * @param ps The {@link PreparedStatement} to be executed.
-         * @param rsh The handler that converts the results into an object.
-         * @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.
-         */
-        public QueryCallableStatement(final Connection conn, final boolean closeConn, final PreparedStatement ps,
-                final ResultSetHandler<T> rsh, final String sql, final Object... params) {
-            this.sql = sql;
-            this.params = params;
-            this.conn = conn;
-            this.closeConn = closeConn;
-            this.ps = ps;
-            this.rsh = rsh;
-        }
-
-        /**
-         * The actual call to {@code handle()} method.
-         *
-         * @return an array of update counts containing one element for each command in the batch.
-         * @throws SQLException if a database access error occurs.
-         * @see ResultSetHandler#handle(ResultSet)
-         */
-        @Override
-        public T call() throws SQLException {
-            ResultSet rs = null;
-            T ret = null;
-
-            try {
-                rs = wrap(ps.executeQuery());
-                ret = rsh.handle(rs);
-            } catch (final SQLException e) {
-                rethrow(e, sql, params);
-            } finally {
-                try {
-                    close(rs);
-                } finally {
-                    close(ps);
-                    if (closeConn) {
-                        close(conn);
-                    }
-                }
-            }
-
-            return ret;
-        }
-
-    }
-
-    /**
-     * 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} 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} 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} is retrieved from the
-     * {@code DataSource} 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}.
-     * @param params Initialize the PreparedStatement's IN parameters with
-     * this array.
-     * @return A {@code Future} 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 {
-        return executorService.submit(new Callable<T>() {
-
-            @Override
-            public T call() throws Exception {
-                return queryRunner.query(sql, rsh, params);
-            }
-
-        });
-    }
-
-    /**
-     * Executes the given SELECT SQL without any replacement parameters.
-     * The {@code Connection} is retrieved from the
-     * {@code DataSource} 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}.
-     *
-     * @return A {@code Future} 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);
-            }
-
-        });
-    }
-
-    /**
-     * @deprecated No longer used by this class. Will be removed in a future version.
-     * Class that encapsulates the continuation for update calls.
-     */
-    @Deprecated
-    protected class UpdateCallableStatement implements Callable<Integer> {
-        private final String sql;
-        private final Object[] params;
-        private final Connection conn;
-        private final boolean closeConn;
-        private final PreparedStatement ps;
-
-        /**
-         *
-         *
-         * @param conn The connection to use for the batch call.
-         * @param closeConn True if the connection should be closed, false otherwise.
-         * @param ps The {@link PreparedStatement} to be executed.
-         * @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.
-         */
-        public UpdateCallableStatement(final Connection conn, final boolean closeConn, final PreparedStatement ps, final String sql, final Object... params) {
-            this.sql = sql;
-            this.params = params;
-            this.conn = conn;
-            this.closeConn = closeConn;
-            this.ps = ps;
-        }
-
-        /**
-         * The actual call to {@code executeUpdate()} method.
-         *
-         * @return either (1) the row count for SQL Data Manipulation Language (DML) statements or
-         *                (2) 0 for SQL statements that return nothing
-         * @throws SQLException if a database access error occurs.
-         * @see PreparedStatement#executeUpdate()
-         */
-        @Override
-        public Integer call() throws SQLException {
-            int rows = 0;
-
-            try {
-                rows = ps.executeUpdate();
-            } catch (final SQLException e) {
-                rethrow(e, sql, params);
-            } finally {
-                close(ps);
-                if (closeConn) {
-                    close(conn);
-                }
-            }
-
-            return Integer.valueOf(rows);
-        }
-
-    }
-
-    /**
-     * 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} 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 Integer.valueOf(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} 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 Integer.valueOf(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} 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 Integer.valueOf(queryRunner.update(conn, sql, params));
-            }
-
-        });
-    }
-
-    /**
-     * Executes the given INSERT, UPDATE, or DELETE SQL statement without
-     * any replacement parameters. The {@code Connection} is retrieved
-     * from the {@code DataSource} set in the constructor.  This
-     * {@code Connection} 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} which returns the number of rows updated.
-     */
-    public Future<Integer> update(final String sql) throws SQLException {
-        return executorService.submit(new Callable<Integer>() {
-
-            @Override
-            public Integer call() throws Exception {
-                return Integer.valueOf(queryRunner.update(sql));
-            }
-
-        });
-    }
-
-    /**
-     * Executes the given INSERT, UPDATE, or DELETE SQL statement with
-     * a single replacement parameter.  The {@code Connection} is
-     * retrieved from the {@code DataSource} set in the constructor.
-     * This {@code Connection} 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} 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 Integer.valueOf(queryRunner.update(sql, param));
-            }
-
-        });
-    }
-
-    /**
-     * Executes the given INSERT, UPDATE, or DELETE SQL statement.  The
-     * {@code Connection} is retrieved from the {@code DataSource}
-     * set in the constructor.  This {@code Connection} 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.
-     * @throws SQLException if a database access error occurs
-     * @return A {@code Future} 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 Integer.valueOf(queryRunner.update(sql, params));
-            }
-
-        });
-    }
-
-    /**
-     * Executes {@link QueryRunner#insert(String, ResultSetHandler)} asynchronously.
-     *
-     * @param <T> Return type expected
-     * @param sql SQL insert statement to execute
-     * @param rsh {@link ResultSetHandler} for handling the results
-     * @return {@link Future} that executes a query runner insert
-     * @see QueryRunner#insert(String, ResultSetHandler)
-     * @throws SQLException if a database access error occurs
-     * @since 1.6
-     */
-    public <T> Future<T> insert(final String sql, final ResultSetHandler<T> rsh) throws SQLException {
-        return executorService.submit(new Callable<T>() {
-
-            @Override
-            public T call() throws Exception {
-                return queryRunner.insert(sql, rsh);
-            }
-
-        });
-    }
-
-    /**
-     * Executes {@link QueryRunner#insert(String, ResultSetHandler, Object...)} asynchronously.
-     *
-     * @param <T> Return type expected
-     * @param sql SQL insert statement to execute
-     * @param rsh {@link ResultSetHandler} for handling the results
-     * @param params Parameter values for substitution in the SQL statement
-     * @return {@link Future} that executes a query runner insert
-     * @see QueryRunner#insert(String, ResultSetHandler, Object...)
-     * @throws SQLException if a database access error occurs
-     * @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.
-     *
-     * @param <T> Return type expected
-     * @param conn {@link Connection} to use to execute the SQL statement
-     * @param sql SQL insert statement to execute
-     * @param rsh {@link ResultSetHandler} for handling the results
-     * @return {@link Future} that executes a query runner insert
-     * @see QueryRunner#insert(Connection, String, ResultSetHandler)
-     * @throws SQLException if a database access error occurs
-     * @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.
-     *
-     * @param <T> Return type expected
-     * @param conn {@link Connection} to use to execute the SQL statement
-     * @param sql SQL insert statement to execute
-     * @param rsh {@link ResultSetHandler} for handling the results
-     * @param params Parameter values for substitution in the SQL statement
-     * @return {@link Future} that executes a query runner insert
-     * @see QueryRunner#insert(Connection, String, ResultSetHandler, Object...)
-     * @throws SQLException if a database access error occurs
-     * @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);
-            }
-        });
-    }
-
-    /**
-     * {@link QueryRunner#insertBatch(String, ResultSetHandler, Object[][])} asynchronously.
-     *
-     * @param <T> Return type expected
-     * @param sql SQL insert statement to execute
-     * @param rsh {@link ResultSetHandler} for handling the results
-     * @param params An array of query replacement parameters.  Each row in
-     *        this array is one set of batch replacement values.
-     * @return {@link Future} that executes a query runner batch insert
-     * @see QueryRunner#insertBatch(String, ResultSetHandler, Object[][])
-     * @throws SQLException if a database access error occurs
-     * @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.
-     *
-     * @param <T> Return type expected
-     * @param conn {@link Connection} to use to execute the SQL statement
-     * @param sql SQL insert statement to execute
-     * @param rsh {@link ResultSetHandler} for handling the results
-     * @param params An array of query replacement parameters.  Each row in
-     *        this array is one set of batch replacement values.
-     * @return {@link Future} that executes a query runner batch insert
-     * @see QueryRunner#insertBatch(Connection, String, ResultSetHandler, Object[][])
-     * @throws SQLException if a database access error occurs
-     * @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);
-            }
-        });
-    }
-
-}
+/*
+ * 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.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import javax.sql.DataSource;
+
+/**
+ * Executes SQL queries with pluggable strategies for handling
+ * {@code ResultSet}s.  This class is thread safe.
+ *
+ * @see ResultSetHandler
+ * @since 1.4
+ */
+public class AsyncQueryRunner extends AbstractQueryRunner {
+
+    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 1.5
+     */
+    public AsyncQueryRunner(final ExecutorService executorService, final QueryRunner queryRunner) {
+        this.executorService = executorService;
+        this.queryRunner = queryRunner;
+    }
+
+    /**
+     * Constructor for AsyncQueryRunner.
+     *
+     * @param executorService the {@code ExecutorService} instance used to run JDBC invocations concurrently.
+     */
+    public AsyncQueryRunner(final ExecutorService executorService) {
+        this(null, false, executorService);
+    }
+
+    /**
+     * @deprecated Use {@link #AsyncQueryRunner(ExecutorService, QueryRunner)} instead.
+     * Constructor for AsyncQueryRunner that controls the use of {@code ParameterMetaData}.
+     *
+     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
+     * if {@code pmdKnownBroken} 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.
+     * @param executorService the {@code ExecutorService} instance used to run JDBC invocations concurrently.
+     */
+    @Deprecated
+    public AsyncQueryRunner(final boolean pmdKnownBroken, final ExecutorService executorService) {
+        this(null, pmdKnownBroken, executorService);
+    }
+
+    /**
+     * @deprecated Use {@link #AsyncQueryRunner(ExecutorService, QueryRunner)} instead.
+     * Constructor for AsyncQueryRunner that takes a {@code DataSource}.
+     *
+     * Methods that do not take a {@code Connection} parameter will retrieve connections from this
+     * {@code DataSource}.
+     *
+     * @param ds The {@code DataSource} to retrieve connections from.
+     * @param executorService the {@code ExecutorService} instance used to run JDBC invocations concurrently.
+     */
+    @Deprecated
+    public AsyncQueryRunner(final DataSource ds, final ExecutorService executorService) {
+        this(ds, false, executorService);
+    }
+
+    /**
+     * @deprecated Use {@link #AsyncQueryRunner(ExecutorService, QueryRunner)} instead.
+     * Constructor for AsyncQueryRunner that take a {@code DataSource} and controls the use of {@code ParameterMetaData}.
+     * Methods that do not take a {@code Connection} parameter will retrieve connections from this
+     * {@code DataSource}.
+     *
+     * @param ds The {@code DataSource} to retrieve connections from.
+     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
+     * if {@code pmdKnownBroken} 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.
+     * @param executorService the {@code ExecutorService} instance used to run JDBC invocations concurrently.
+     */
+    @Deprecated
+    public AsyncQueryRunner(final DataSource ds, final boolean pmdKnownBroken, final ExecutorService executorService) {
+        super(ds, pmdKnownBroken);
+        this.executorService = executorService;
+        this.queryRunner = new QueryRunner(ds, pmdKnownBroken);
+    }
+
+    /**
+     * @deprecated No longer used by this class. Will be removed in a future version.
+     * Class that encapsulates the continuation for batch calls.
+     */
+    @Deprecated
+    protected class BatchCallableStatement implements Callable<int[]> {
+        private final String sql;
+        private final Object[][] params;
+        private final Connection conn;
+        private final boolean closeConn;
+        private final PreparedStatement ps;
+
+        /**
+         * Creates a new BatchCallableStatement instance.
+         *
+         * @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.
+         * @param conn The connection to use for the batch call.
+         * @param closeConn True if the connection should be closed, false otherwise.
+         * @param ps The {@link PreparedStatement} to be executed.
+         */
+        public BatchCallableStatement(final String sql, final Object[][] params, final Connection conn, final boolean closeConn, final PreparedStatement ps) {
+            this.sql = sql;
+            this.params = params.clone();
+            this.conn = conn;
+            this.closeConn = closeConn;
+            this.ps = ps;
+        }
+
+        /**
+         * The actual call to executeBatch.
+         *
+         * @return an array of update counts containing one element for each command in the batch.
+         * @throws SQLException if a database access error occurs or one of the commands sent to the database fails.
+         * @see PreparedStatement#executeBatch()
+         */
+        @Override
+        public int[] call() throws SQLException {
+            int[] ret = null;
+
+            try {
+                ret = ps.executeBatch();
+            } catch (final SQLException e) {
+                rethrow(e, sql, (Object[])params);
+            } finally {
+                close(ps);
+                if (closeConn) {
+                    close(conn);
+                }
+            }
+
+            return ret;
+        }
+    }
+
+    /**
+     * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
+     *
+     * @param conn The {@code Connection} to use to run the query.  The caller is
+     * responsible for closing this Connection.
+     * @param sql The SQL to execute.
+     * @param params An array of query replacement parameters.  Each row in
+     * this array is one set of batch replacement values.
+     * @return A {@code Future} 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} is retrieved from the {@code DataSource}
+     * set in the constructor.  This {@code Connection} 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} 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);
+            }
+
+        });
+    }
+
+    /**
+     * Class that encapsulates the continuation for query calls.
+     * @param <T> The type of the result from the call to handle.
+     */
+    protected class QueryCallableStatement<T> implements Callable<T> {
+        private final String sql;
+        private final Object[] params;
+        private final Connection conn;
+        private final boolean closeConn;
+        private final PreparedStatement ps;
+        private final ResultSetHandler<T> rsh;
+
+        /**
+         * Creates a new {@code QueryCallableStatement} instance.
+         *
+         * @param conn The connection to use for the batch call.
+         * @param closeConn True if the connection should be closed, false otherwise.
+         * @param ps The {@link PreparedStatement} to be executed.
+         * @param rsh The handler that converts the results into an object.
+         * @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.
+         */
+        public QueryCallableStatement(final Connection conn, final boolean closeConn, final PreparedStatement ps,
+                final ResultSetHandler<T> rsh, final String sql, final Object... params) {
+            this.sql = sql;
+            this.params = params;
+            this.conn = conn;
+            this.closeConn = closeConn;
+            this.ps = ps;
+            this.rsh = rsh;
+        }
+
+        /**
+         * The actual call to {@code handle()} method.
+         *
+         * @return an array of update counts containing one element for each command in the batch.
+         * @throws SQLException if a database access error occurs.
+         * @see ResultSetHandler#handle(ResultSet)
+         */
+        @Override
+        public T call() throws SQLException {
+            ResultSet rs = null;
+            T ret = null;
+
+            try {
+                rs = wrap(ps.executeQuery());
+                ret = rsh.handle(rs);
+            } catch (final SQLException e) {
+                rethrow(e, sql, params);
+            } finally {
+                try {
+                    close(rs);
+                } finally {
+                    close(ps);
+                    if (closeConn) {
+                        close(conn);
+                    }
+                }
+            }
+
+            return ret;
+        }
+
+    }
+
+    /**
+     * 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} 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} 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} is retrieved from the
+     * {@code DataSource} 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}.
+     * @param params Initialize the PreparedStatement's IN parameters with
+     * this array.
+     * @return A {@code Future} 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 {
+        return executorService.submit(new Callable<T>() {
+
+            @Override
+            public T call() throws Exception {
+                return queryRunner.query(sql, rsh, params);
+            }
+
+        });
+    }
+
+    /**
+     * Executes the given SELECT SQL without any replacement parameters.
+     * The {@code Connection} is retrieved from the
+     * {@code DataSource} 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}.
+     *
+     * @return A {@code Future} 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);
+            }
+
+        });
+    }
+
+    /**
+     * @deprecated No longer used by this class. Will be removed in a future version.
+     * Class that encapsulates the continuation for update calls.
+     */
+    @Deprecated
+    protected class UpdateCallableStatement implements Callable<Integer> {
+        private final String sql;
+        private final Object[] params;
+        private final Connection conn;
+        private final boolean closeConn;
+        private final PreparedStatement ps;
+
+        /**
+         *
+         *
+         * @param conn The connection to use for the batch call.
+         * @param closeConn True if the connection should be closed, false otherwise.
+         * @param ps The {@link PreparedStatement} to be executed.
+         * @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.
+         */
+        public UpdateCallableStatement(final Connection conn, final boolean closeConn, final PreparedStatement ps, final String sql, final Object... params) {
+            this.sql = sql;
+            this.params = params;
+            this.conn = conn;
+            this.closeConn = closeConn;
+            this.ps = ps;
+        }
+
+        /**
+         * The actual call to {@code executeUpdate()} method.
+         *
+         * @return either (1) the row count for SQL Data Manipulation Language (DML) statements or
+         *                (2) 0 for SQL statements that return nothing
+         * @throws SQLException if a database access error occurs.
+         * @see PreparedStatement#executeUpdate()
+         */
+        @Override
+        public Integer call() throws SQLException {
+            int rows = 0;
+
+            try {
+                rows = ps.executeUpdate();
+            } catch (final SQLException e) {
+                rethrow(e, sql, params);
+            } finally {
+                close(ps);
+                if (closeConn) {
+                    close(conn);
+                }
+            }
+
+            return Integer.valueOf(rows);
+        }
+
+    }
+
+    /**
+     * 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} 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 Integer.valueOf(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} 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 Integer.valueOf(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} 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 Integer.valueOf(queryRunner.update(conn, sql, params));
+            }
+
+        });
+    }
+
+    /**
+     * Executes the given INSERT, UPDATE, or DELETE SQL statement without
+     * any replacement parameters. The {@code Connection} is retrieved
+     * from the {@code DataSource} set in the constructor.  This
+     * {@code Connection} 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} which returns the number of rows updated.
+     */
+    public Future<Integer> update(final String sql) throws SQLException {
+        return executorService.submit(new Callable<Integer>() {
+
+            @Override
+            public Integer call() throws Exception {
+                return Integer.valueOf(queryRunner.update(sql));
+            }
+
+        });
+    }
+
+    /**
+     * Executes the given INSERT, UPDATE, or DELETE SQL statement with
+     * a single replacement parameter.  The {@code Connection} is
+     * retrieved from the {@code DataSource} set in the constructor.
+     * This {@code Connection} 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} 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 Integer.valueOf(queryRunner.update(sql, param));
+            }
+
+        });
+    }
+
+    /**
+     * Executes the given INSERT, UPDATE, or DELETE SQL statement.  The
+     * {@code Connection} is retrieved from the {@code DataSource}
+     * set in the constructor.  This {@code Connection} 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.
+     * @throws SQLException if a database access error occurs
+     * @return A {@code Future} 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 Integer.valueOf(queryRunner.update(sql, params));
+            }
+
+        });
+    }
+
+    /**
+     * Executes {@link QueryRunner#insert(String, ResultSetHandler)} asynchronously.
+     *
+     * @param <T> Return type expected
+     * @param sql SQL insert statement to execute
+     * @param rsh {@link ResultSetHandler} for handling the results
+     * @return {@link Future} that executes a query runner insert
+     * @see QueryRunner#insert(String, ResultSetHandler)
+     * @throws SQLException if a database access error occurs
+     * @since 1.6
+     */
+    public <T> Future<T> insert(final String sql, final ResultSetHandler<T> rsh) throws SQLException {
+        return executorService.submit(new Callable<T>() {
+
+            @Override
+            public T call() throws Exception {
+                return queryRunner.insert(sql, rsh);
+            }
+
+        });
+    }
+
+    /**
+     * Executes {@link QueryRunner#insert(String, ResultSetHandler, Object...)} asynchronously.
+     *
+     * @param <T> Return type expected
+     * @param sql SQL insert statement to execute
+     * @param rsh {@link ResultSetHandler} for handling the results
+     * @param params Parameter values for substitution in the SQL statement
+     * @return {@link Future} that executes a query runner insert
+     * @see QueryRunner#insert(String, ResultSetHandler, Object...)
+     * @throws SQLException if a database access error occurs
+     * @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.
+     *
+     * @param <T> Return type expected
+     * @param conn {@link Connection} to use to execute the SQL statement
+     * @param sql SQL insert statement to execute
+     * @param rsh {@link ResultSetHandler} for handling the results
+     * @return {@link Future} that executes a query runner insert
+     * @see QueryRunner#insert(Connection, String, ResultSetHandler)
+     * @throws SQLException if a database access error occurs
+     * @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.
+     *
+     * @param <T> Return type expected
+     * @param conn {@link Connection} to use to execute the SQL statement
+     * @param sql SQL insert statement to execute
+     * @param rsh {@link ResultSetHandler} for handling the results
+     * @param params Parameter values for substitution in the SQL statement
+     * @return {@link Future} that executes a query runner insert
+     * @see QueryRunner#insert(Connection, String, ResultSetHandler, Object...)
+     * @throws SQLException if a database access error occurs
+     * @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);
+            }
+        });
+    }
+
+    /**
+     * {@link QueryRunner#insertBatch(String, ResultSetHandler, Object[][])} asynchronously.
+     *
+     * @param <T> Return type expected
+     * @param sql SQL insert statement to execute
+     * @param rsh {@link ResultSetHandler} for handling the results
+     * @param params An array of query replacement parameters.  Each row in
+     *        this array is one set of batch replacement values.
+     * @return {@link Future} that executes a query runner batch insert
+     * @see QueryRunner#insertBatch(String, ResultSetHandler, Object[][])
+     * @throws SQLException if a database access error occurs
+     * @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.
+     *
+     * @param <T> Return type expected
+     * @param conn {@link Connection} to use to execute the SQL statement
+     * @param sql SQL insert statement to execute
+     * @param rsh {@link ResultSetHandler} for handling the results
+     * @param params An array of query replacement parameters.  Each row in
+     *        this array is one set of batch replacement values.
+     * @return {@link Future} that executes a query runner batch insert
+     * @see QueryRunner#insertBatch(Connection, String, ResultSetHandler, Object[][])
+     * @throws SQLException if a database access error occurs
+     * @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);
+            }
+        });
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/dbutils/BasicRowProcessor.java b/src/main/java/org/apache/commons/dbutils/BasicRowProcessor.java
index fdfd0da..8af06c2 100644
--- a/src/main/java/org/apache/commons/dbutils/BasicRowProcessor.java
+++ b/src/main/java/org/apache/commons/dbutils/BasicRowProcessor.java
@@ -80,7 +80,7 @@ public class BasicRowProcessor implements RowProcessor {
      * BasicRowProcessor constructor.
      * @param convert The BeanProcessor to use when converting columns to
      * bean properties.
-     * @since DbUtils 1.1
+     * @since 1.1
      */
     public BasicRowProcessor(final BeanProcessor convert) {
         this.convert = convert;
diff --git a/src/main/java/org/apache/commons/dbutils/BeanProcessor.java b/src/main/java/org/apache/commons/dbutils/BeanProcessor.java
index 1542475..884c801 100644
--- a/src/main/java/org/apache/commons/dbutils/BeanProcessor.java
+++ b/src/main/java/org/apache/commons/dbutils/BeanProcessor.java
@@ -49,7 +49,7 @@ import java.util.ServiceLoader;
  *
  * @see BasicRowProcessor
  *
- * @since DbUtils 1.1
+ * @since 1.1
  */
 public class BeanProcessor {
 
diff --git a/src/main/java/org/apache/commons/dbutils/DbUtils.java b/src/main/java/org/apache/commons/dbutils/DbUtils.java
index fa290dd..d178db0 100644
--- a/src/main/java/org/apache/commons/dbutils/DbUtils.java
+++ b/src/main/java/org/apache/commons/dbutils/DbUtils.java
@@ -1,426 +1,426 @@
-/*
- * 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 java.sql.DriverManager.registerDriver;
-
-import java.io.PrintWriter;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.sql.Connection;
-import java.sql.Driver;
-import java.sql.DriverPropertyInfo;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.SQLFeatureNotSupportedException;
-import java.sql.Statement;
-import java.util.logging.Logger;
-import java.util.Properties;
-
-/**
- * A collection of JDBC helper methods.  This class is thread safe.
- */
-public final class DbUtils {
-
-    /**
-     * Default constructor.
-     *
-     * Utility classes should not have a public or default constructor,
-     * but this one preserves retro-compatibility.
-     *
-     * @since 1.4
-     */
-    public DbUtils() {
-        // do nothing
-    }
-
-    /**
-     * Close a {@code Connection}, avoid closing if null.
-     *
-     * @param conn Connection to close.
-     * @throws SQLException if a database access error occurs
-     */
-    public static void close(final Connection conn) throws SQLException {
-        if (conn != null) {
-            conn.close();
-        }
-    }
-
-    /**
-     * Close a {@code ResultSet}, avoid closing if null.
-     *
-     * @param rs ResultSet to close.
-     * @throws SQLException if a database access error occurs
-     */
-    public static void close(final ResultSet rs) throws SQLException {
-        if (rs != null) {
-            rs.close();
-        }
-    }
-
-    /**
-     * Close a {@code Statement}, avoid closing if null.
-     *
-     * @param stmt Statement to close.
-     * @throws SQLException if a database access error occurs
-     */
-    public static void close(final Statement stmt) throws SQLException {
-        if (stmt != null) {
-            stmt.close();
-        }
-    }
-
-    /**
-     * Close a {@code Connection}, avoid closing if null and hide
-     * any SQLExceptions that occur.
-     *
-     * @param conn Connection to close.
-     */
-    public static void closeQuietly(final Connection conn) {
-        try {
-            close(conn);
-        } catch (final SQLException e) { // NOPMD
-            // quiet
-        }
-    }
-
-    /**
-     * Close a {@code Connection}, {@code Statement} and
-     * {@code ResultSet}.  Avoid closing if null and hide any
-     * SQLExceptions that occur.
-     *
-     * @param conn Connection to close.
-     * @param stmt Statement to close.
-     * @param rs ResultSet to close.
-     */
-    public static void closeQuietly(final Connection conn, final Statement stmt,
-            final ResultSet rs) {
-
-        try {
-            closeQuietly(rs);
-        } finally {
-            try {
-                closeQuietly(stmt);
-            } finally {
-                closeQuietly(conn);
-            }
-        }
-
-    }
-
-    /**
-     * Close a {@code ResultSet}, avoid closing if null and hide any
-     * SQLExceptions that occur.
-     *
-     * @param rs ResultSet to close.
-     */
-    public static void closeQuietly(final ResultSet rs) {
-        try {
-            close(rs);
-        } catch (final SQLException e) { // NOPMD
-            // quiet
-        }
-    }
-
-    /**
-     * Close a {@code Statement}, avoid closing if null and hide
-     * any SQLExceptions that occur.
-     *
-     * @param stmt Statement to close.
-     */
-    public static void closeQuietly(final Statement stmt) {
-        try {
-            close(stmt);
-        } catch (final SQLException e) { // NOPMD
-            // quiet
-        }
-    }
-
-    /**
-     * Commits a {@code Connection} then closes it, avoid closing if null.
-     *
-     * @param conn Connection to close.
-     * @throws SQLException if a database access error occurs
-     */
-    public static void commitAndClose(final Connection conn) throws SQLException {
-        if (conn != null) {
-            try {
-                conn.commit();
-            } finally {
-                conn.close();
-            }
-        }
-    }
-
-    /**
-     * Commits a {@code Connection} then closes it, avoid closing if null
-     * and hide any SQLExceptions that occur.
-     *
-     * @param conn Connection to close.
-     */
-    public static void commitAndCloseQuietly(final Connection conn) {
-        try {
-            commitAndClose(conn);
-        } catch (final SQLException e) { // NOPMD
-            // quiet
-        }
-    }
-
-    /**
-     * Loads and registers a database driver class.
-     * If this succeeds, it returns true, else it returns false.
-     *
-     * @param driverClassName of driver to load
-     * @return boolean {@code true} if the driver was found, otherwise {@code false}
-     */
-    public static boolean loadDriver(final String driverClassName) {
-        return loadDriver(DbUtils.class.getClassLoader(), driverClassName);
-    }
-
-    /**
-     * Loads and registers a database driver class.
-     * If this succeeds, it returns true, else it returns false.
-     *
-     * @param classLoader the class loader used to load the driver class
-     * @param driverClassName of driver to load
-     * @return boolean {@code true} if the driver was found, otherwise {@code false}
-     * @since 1.4
-     */
-    public static boolean loadDriver(final ClassLoader classLoader, final String driverClassName) {
-        try {
-            final Class<?> loadedClass = classLoader.loadClass(driverClassName);
-
-            if (!Driver.class.isAssignableFrom(loadedClass)) {
-                return false;
-            }
-
-            @SuppressWarnings("unchecked") // guarded by previous check
-            final
-            Class<Driver> driverClass = (Class<Driver>) loadedClass;
-            final Constructor<Driver> driverConstructor = driverClass.getConstructor();
-
-            // make Constructor accessible if it is private
-            @SuppressWarnings("deprecation")
-            // TODO This is deprecated in Java9 and canAccess() should be used. Adding suppression for building on
-            //      later JDKs without a warning.
-            final boolean isConstructorAccessible = driverConstructor.isAccessible();
-            if (!isConstructorAccessible) {
-                driverConstructor.setAccessible(true);
-            }
-
-            try {
-                final Driver driver = driverConstructor.newInstance();
-                registerDriver(new DriverProxy(driver));
-            } finally {
-                driverConstructor.setAccessible(isConstructorAccessible);
-            }
-
-            return true;
-        } catch (final Exception e) {
-            return false;
-        }
-    }
-
-    /**
-     * Print the stack trace for a SQLException to STDERR.
-     *
-     * @param e SQLException to print stack trace of
-     */
-    public static void printStackTrace(final SQLException e) {
-        printStackTrace(e, new PrintWriter(System.err));
-    }
-
-    /**
-     * Print the stack trace for a SQLException to a
-     * specified PrintWriter.
-     *
-     * @param e SQLException to print stack trace of
-     * @param pw PrintWriter to print to
-     */
-    public static void printStackTrace(final SQLException e, final PrintWriter pw) {
-
-        SQLException next = e;
-        while (next != null) {
-            next.printStackTrace(pw);
-            next = next.getNextException();
-            if (next != null) {
-                pw.println("Next SQLException:");
-            }
-        }
-    }
-
-    /**
-     * Print warnings on a Connection to STDERR.
-     *
-     * @param conn Connection to print warnings from
-     */
-    public static void printWarnings(final Connection conn) {
-        printWarnings(conn, new PrintWriter(System.err));
-    }
-
-    /**
-     * Print warnings on a Connection to a specified PrintWriter.
-     *
-     * @param conn Connection to print warnings from
-     * @param pw PrintWriter to print to
-     */
-    public static void printWarnings(final Connection conn, final PrintWriter pw) {
-        if (conn != null) {
-            try {
-                printStackTrace(conn.getWarnings(), pw);
-            } catch (final SQLException e) {
-                printStackTrace(e, pw);
-            }
-        }
-    }
-
-    /**
-     * Rollback any changes made on the given connection.
-     * @param conn Connection to rollback.  A null value is legal.
-     * @throws SQLException if a database access error occurs
-     */
-    public static void rollback(final Connection conn) throws SQLException {
-        if (conn != null) {
-            conn.rollback();
-        }
-    }
-
-    /**
-     * Performs a rollback on the {@code Connection} then closes it,
-     * avoid closing if null.
-     *
-     * @param conn Connection to rollback.  A null value is legal.
-     * @throws SQLException if a database access error occurs
-     * @since DbUtils 1.1
-     */
-    public static void rollbackAndClose(final Connection conn) throws SQLException {
-        if (conn != null) {
-            try {
-                conn.rollback();
-            } finally {
-                conn.close();
-            }
-        }
-    }
-
-    /**
-     * Performs a rollback on the {@code Connection} then closes it,
-     * avoid closing if null and hide any SQLExceptions that occur.
-     *
-     * @param conn Connection to rollback.  A null value is legal.
-     * @since DbUtils 1.1
-     */
-    public static void rollbackAndCloseQuietly(final Connection conn) {
-        try {
-            rollbackAndClose(conn);
-        } catch (final SQLException e) { // NOPMD
-            // quiet
-        }
-    }
-
-    /**
-     * Simple {@link Driver} proxy class that proxies a JDBC Driver loaded dynamically.
-     *
-     * @since 1.6
-     */
-    static final class DriverProxy implements Driver {
-
-        private boolean parentLoggerSupported = true;
-
-        /**
-         * The adapted JDBC Driver loaded dynamically.
-         */
-        private final Driver adapted;
-
-        /**
-         * Creates a new JDBC Driver that adapts a JDBC Driver loaded dynamically.
-         *
-         * @param adapted the adapted JDBC Driver loaded dynamically.
-         */
-        public DriverProxy(final Driver adapted) {
-            this.adapted = adapted;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public boolean acceptsURL(final String url) throws SQLException {
-            return adapted.acceptsURL(url);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public Connection connect(final String url, final Properties info) throws SQLException {
-            return adapted.connect(url, info);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public int getMajorVersion() {
-            return adapted.getMajorVersion();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public int getMinorVersion() {
-            return adapted.getMinorVersion();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public DriverPropertyInfo[] getPropertyInfo(final String url, final Properties info) throws SQLException {
-            return adapted.getPropertyInfo(url, info);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public boolean jdbcCompliant() {
-            return adapted.jdbcCompliant();
-        }
-
-        /**
-         * Java 1.7 method.
-         */
-        @Override
-        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
-            if (parentLoggerSupported) {
-                try {
-                    final Method method = adapted.getClass().getMethod("getParentLogger");
-                    return (Logger)method.invoke(adapted);
-                } catch (final NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-                    parentLoggerSupported = false;
-                    throw new SQLFeatureNotSupportedException(e);
-                }
-            }
-            throw new SQLFeatureNotSupportedException();
-        }
-
-    }
-
-}
+/*
+ * 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 java.sql.DriverManager.registerDriver;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverPropertyInfo;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.Statement;
+import java.util.logging.Logger;
+import java.util.Properties;
+
+/**
+ * A collection of JDBC helper methods.  This class is thread safe.
+ */
+public final class DbUtils {
+
+    /**
+     * Default constructor.
+     *
+     * Utility classes should not have a public or default constructor,
+     * but this one preserves retro-compatibility.
+     *
+     * @since 1.4
+     */
+    public DbUtils() {
+        // do nothing
+    }
+
+    /**
+     * Close a {@code Connection}, avoid closing if null.
+     *
+     * @param conn Connection to close.
+     * @throws SQLException if a database access error occurs
+     */
+    public static void close(final Connection conn) throws SQLException {
+        if (conn != null) {
+            conn.close();
+        }
+    }
+
+    /**
+     * Close a {@code ResultSet}, avoid closing if null.
+     *
+     * @param rs ResultSet to close.
+     * @throws SQLException if a database access error occurs
+     */
+    public static void close(final ResultSet rs) throws SQLException {
+        if (rs != null) {
+            rs.close();
+        }
+    }
+
+    /**
+     * Close a {@code Statement}, avoid closing if null.
+     *
+     * @param stmt Statement to close.
+     * @throws SQLException if a database access error occurs
+     */
+    public static void close(final Statement stmt) throws SQLException {
+        if (stmt != null) {
+            stmt.close();
+        }
+    }
+
+    /**
+     * Close a {@code Connection}, avoid closing if null and hide
+     * any SQLExceptions that occur.
+     *
+     * @param conn Connection to close.
+     */
+    public static void closeQuietly(final Connection conn) {
+        try {
+            close(conn);
+        } catch (final SQLException e) { // NOPMD
+            // quiet
+        }
+    }
+
+    /**
+     * Close a {@code Connection}, {@code Statement} and
+     * {@code ResultSet}.  Avoid closing if null and hide any
+     * SQLExceptions that occur.
+     *
+     * @param conn Connection to close.
+     * @param stmt Statement to close.
+     * @param rs ResultSet to close.
+     */
+    public static void closeQuietly(final Connection conn, final Statement stmt,
+            final ResultSet rs) {
+
+        try {
+            closeQuietly(rs);
+        } finally {
+            try {
+                closeQuietly(stmt);
+            } finally {
+                closeQuietly(conn);
+            }
+        }
+
+    }
+
+    /**
+     * Close a {@code ResultSet}, avoid closing if null and hide any
+     * SQLExceptions that occur.
+     *
+     * @param rs ResultSet to close.
+     */
+    public static void closeQuietly(final ResultSet rs) {
+        try {
+            close(rs);
+        } catch (final SQLException e) { // NOPMD
+            // quiet
+        }
+    }
+
+    /**
+     * Close a {@code Statement}, avoid closing if null and hide
+     * any SQLExceptions that occur.
+     *
+     * @param stmt Statement to close.
+     */
+    public static void closeQuietly(final Statement stmt) {
+        try {
+            close(stmt);
+        } catch (final SQLException e) { // NOPMD
+            // quiet
+        }
+    }
+
+    /**
+     * Commits a {@code Connection} then closes it, avoid closing if null.
+     *
+     * @param conn Connection to close.
+     * @throws SQLException if a database access error occurs
+     */
+    public static void commitAndClose(final Connection conn) throws SQLException {
+        if (conn != null) {
+            try {
+                conn.commit();
+            } finally {
+                conn.close();
+            }
+        }
+    }
+
+    /**
+     * Commits a {@code Connection} then closes it, avoid closing if null
+     * and hide any SQLExceptions that occur.
+     *
+     * @param conn Connection to close.
+     */
+    public static void commitAndCloseQuietly(final Connection conn) {
+        try {
+            commitAndClose(conn);
+        } catch (final SQLException e) { // NOPMD
+            // quiet
+        }
+    }
+
+    /**
+     * Loads and registers a database driver class.
+     * If this succeeds, it returns true, else it returns false.
+     *
+     * @param driverClassName of driver to load
+     * @return boolean {@code true} if the driver was found, otherwise {@code false}
+     */
+    public static boolean loadDriver(final String driverClassName) {
+        return loadDriver(DbUtils.class.getClassLoader(), driverClassName);
+    }
+
+    /**
+     * Loads and registers a database driver class.
+     * If this succeeds, it returns true, else it returns false.
+     *
+     * @param classLoader the class loader used to load the driver class
+     * @param driverClassName of driver to load
+     * @return boolean {@code true} if the driver was found, otherwise {@code false}
+     * @since 1.4
+     */
+    public static boolean loadDriver(final ClassLoader classLoader, final String driverClassName) {
+        try {
+            final Class<?> loadedClass = classLoader.loadClass(driverClassName);
+
+            if (!Driver.class.isAssignableFrom(loadedClass)) {
+                return false;
+            }
+
+            @SuppressWarnings("unchecked") // guarded by previous check
+            final
+            Class<Driver> driverClass = (Class<Driver>) loadedClass;
+            final Constructor<Driver> driverConstructor = driverClass.getConstructor();
+
+            // make Constructor accessible if it is private
+            @SuppressWarnings("deprecation")
+            // TODO This is deprecated in Java9 and canAccess() should be used. Adding suppression for building on
+            //      later JDKs without a warning.
+            final boolean isConstructorAccessible = driverConstructor.isAccessible();
+            if (!isConstructorAccessible) {
+                driverConstructor.setAccessible(true);
+            }
+
+            try {
+                final Driver driver = driverConstructor.newInstance();
+                registerDriver(new DriverProxy(driver));
+            } finally {
+                driverConstructor.setAccessible(isConstructorAccessible);
+            }
+
+            return true;
+        } catch (final Exception e) {
+            return false;
+        }
+    }
+
+    /**
+     * Print the stack trace for a SQLException to STDERR.
+     *
+     * @param e SQLException to print stack trace of
+     */
+    public static void printStackTrace(final SQLException e) {
+        printStackTrace(e, new PrintWriter(System.err));
+    }
+
+    /**
+     * Print the stack trace for a SQLException to a
+     * specified PrintWriter.
+     *
+     * @param e SQLException to print stack trace of
+     * @param pw PrintWriter to print to
+     */
+    public static void printStackTrace(final SQLException e, final PrintWriter pw) {
+
+        SQLException next = e;
+        while (next != null) {
+            next.printStackTrace(pw);
+            next = next.getNextException();
+            if (next != null) {
+                pw.println("Next SQLException:");
+            }
+        }
+    }
+
+    /**
+     * Print warnings on a Connection to STDERR.
+     *
+     * @param conn Connection to print warnings from
+     */
+    public static void printWarnings(final Connection conn) {
+        printWarnings(conn, new PrintWriter(System.err));
+    }
+
+    /**
+     * Print warnings on a Connection to a specified PrintWriter.
+     *
+     * @param conn Connection to print warnings from
+     * @param pw PrintWriter to print to
+     */
+    public static void printWarnings(final Connection conn, final PrintWriter pw) {
+        if (conn != null) {
+            try {
+                printStackTrace(conn.getWarnings(), pw);
+            } catch (final SQLException e) {
+                printStackTrace(e, pw);
+            }
+        }
+    }
+
+    /**
+     * Rollback any changes made on the given connection.
+     * @param conn Connection to rollback.  A null value is legal.
+     * @throws SQLException if a database access error occurs
+     */
+    public static void rollback(final Connection conn) throws SQLException {
+        if (conn != null) {
+            conn.rollback();
+        }
+    }
+
+    /**
+     * Performs a rollback on the {@code Connection} then closes it,
+     * avoid closing if null.
+     *
+     * @param conn Connection to rollback.  A null value is legal.
+     * @throws SQLException if a database access error occurs
+     * @since 1.1
+     */
+    public static void rollbackAndClose(final Connection conn) throws SQLException {
+        if (conn != null) {
+            try {
+                conn.rollback();
+            } finally {
+                conn.close();
+            }
+        }
+    }
+
+    /**
+     * Performs a rollback on the {@code Connection} then closes it,
+     * avoid closing if null and hide any SQLExceptions that occur.
+     *
+     * @param conn Connection to rollback.  A null value is legal.
+     * @since 1.1
+     */
+    public static void rollbackAndCloseQuietly(final Connection conn) {
+        try {
+            rollbackAndClose(conn);
+        } catch (final SQLException e) { // NOPMD
+            // quiet
+        }
+    }
+
+    /**
+     * Simple {@link Driver} proxy class that proxies a JDBC Driver loaded dynamically.
+     *
+     * @since 1.6
+     */
+    static final class DriverProxy implements Driver {
+
+        private boolean parentLoggerSupported = true;
+
+        /**
+         * The adapted JDBC Driver loaded dynamically.
+         */
+        private final Driver adapted;
+
+        /**
+         * Creates a new JDBC Driver that adapts a JDBC Driver loaded dynamically.
+         *
+         * @param adapted the adapted JDBC Driver loaded dynamically.
+         */
+        public DriverProxy(final Driver adapted) {
+            this.adapted = adapted;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public boolean acceptsURL(final String url) throws SQLException {
+            return adapted.acceptsURL(url);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public Connection connect(final String url, final Properties info) throws SQLException {
+            return adapted.connect(url, info);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public int getMajorVersion() {
+            return adapted.getMajorVersion();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public int getMinorVersion() {
+            return adapted.getMinorVersion();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public DriverPropertyInfo[] getPropertyInfo(final String url, final Properties info) throws SQLException {
+            return adapted.getPropertyInfo(url, info);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public boolean jdbcCompliant() {
+            return adapted.jdbcCompliant();
+        }
+
+        /**
+         * Java 1.7 method.
+         */
+        @Override
+        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+            if (parentLoggerSupported) {
+                try {
+                    final Method method = adapted.getClass().getMethod("getParentLogger");
+                    return (Logger)method.invoke(adapted);
+                } catch (final NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+                    parentLoggerSupported = false;
+                    throw new SQLFeatureNotSupportedException(e);
+                }
+            }
+            throw new SQLFeatureNotSupportedException();
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/dbutils/QueryLoader.java b/src/main/java/org/apache/commons/dbutils/QueryLoader.java
index efc2ef6..c670404 100644
--- a/src/main/java/org/apache/commons/dbutils/QueryLoader.java
+++ b/src/main/java/org/apache/commons/dbutils/QueryLoader.java
@@ -1,142 +1,142 @@
-/*
- * 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.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-import java.util.regex.Pattern;
-
-/**
- * {@code QueryLoader} is a registry for sets of queries so
- * that multiple copies of the same queries aren't loaded into memory.
- * This implementation loads properties files filled with query name to
- * SQL mappings.  This class is thread safe.
- */
-public class QueryLoader {
-
-    /**
-     * The Singleton instance of this class.
-     */
-    private static final QueryLoader instance = new QueryLoader();
-
-    /**
-     * Matches .xml file extensions.
-     */
-    private static final Pattern dotXml = Pattern.compile(".+\\.[xX][mM][lL]");
-
-    /**
-     * Return an instance of this class.
-     * @return The Singleton instance.
-     */
-    public static QueryLoader instance() {
-        return instance;
-    }
-
-    /**
-     * Maps query set names to Maps of their queries.
-     */
-    private final Map<String, Map<String, String>> queries = new HashMap<>();
-
-    /**
-     * QueryLoader constructor.
-     */
-    protected QueryLoader() {
-    }
-
-    /**
-     * Loads a Map of query names to SQL values.  The Maps are cached so a
-     * subsequent request to load queries from the same path will return
-     * the cached Map.  The properties file to load can be in either
-     * line-oriented or XML format.  XML formatted properties files must use a
-     * {@code .xml} file extension.
-     *
-     * @param path The path that the ClassLoader will use to find the file.
-     * This is <strong>not</strong> a file system path.  If you had a jarred
-     * Queries.properties file in the com.yourcorp.app.jdbc package you would
-     * pass "/com/yourcorp/app/jdbc/Queries.properties" to this method.
-     * @throws IOException if a file access error occurs
-     * @throws IllegalArgumentException if the ClassLoader can't find a file at
-     * the given path.
-     * @throws java.util.InvalidPropertiesFormatException if the XML properties file is
-     * invalid
-     * @return Map of query names to SQL values
-     * @see java.util.Properties
-     */
-    public synchronized Map<String, String> load(final String path) throws IOException {
-
-        Map<String, String> queryMap = this.queries.get(path);
-
-        if (queryMap == null) {
-            queryMap = this.loadQueries(path);
-            this.queries.put(path, queryMap);
-        }
-
-        return queryMap;
-    }
-
-    /**
-     * Loads a set of named queries into a Map object.  This implementation
-     * reads a properties file at the given path.  The properties file can be
-     * in either line-oriented or XML format.  XML formatted properties files
-     * must use a {@code .xml} file extension.
-
-     * @param path The path that the ClassLoader will use to find the file.
-     * @throws IOException if a file access error occurs
-     * @throws IllegalArgumentException if the ClassLoader can't find a file at
-     * the given path.
-     * @throws java.util.InvalidPropertiesFormatException if the XML properties file is
-     * invalid
-     * @since DbUtils 1.1
-     * @return Map of query names to SQL values
-     * @see java.util.Properties
-     */
-    protected Map<String, String> loadQueries(final String path) throws IOException {
-        // Findbugs flags getClass().getResource as a bad practice; maybe we should change the API?
-        final Properties props;
-        try (InputStream in = getClass().getResourceAsStream(path)) {
-
-            if (in == null) {
-                throw new IllegalArgumentException(path + " not found.");
-            }
-            props = new Properties();
-            if (dotXml.matcher(path).matches()) {
-                props.loadFromXML(in);
-            } else {
-                props.load(in);
-            }
-        }
-
-        // Copy to HashMap for better performance
-
-        @SuppressWarnings({"rawtypes", "unchecked" }) // load() always creates <String,String> entries
-        final
-        HashMap<String, String> hashMap = new HashMap(props);
-        return hashMap;
-    }
-
-    /**
-     * Removes the queries for the given path from the cache.
-     * @param path The path that the queries were loaded from.
-     */
-    public synchronized void unload(final String path) {
-        this.queries.remove(path);
-    }
-
-}
+/*
+ * 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.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Pattern;
+
+/**
+ * {@code QueryLoader} is a registry for sets of queries so
+ * that multiple copies of the same queries aren't loaded into memory.
+ * This implementation loads properties files filled with query name to
+ * SQL mappings.  This class is thread safe.
+ */
+public class QueryLoader {
+
+    /**
+     * The Singleton instance of this class.
+     */
+    private static final QueryLoader instance = new QueryLoader();
+
+    /**
+     * Matches .xml file extensions.
+     */
+    private static final Pattern dotXml = Pattern.compile(".+\\.[xX][mM][lL]");
+
+    /**
+     * Return an instance of this class.
+     * @return The Singleton instance.
+     */
+    public static QueryLoader instance() {
+        return instance;
+    }
+
+    /**
+     * Maps query set names to Maps of their queries.
+     */
+    private final Map<String, Map<String, String>> queries = new HashMap<>();
+
+    /**
+     * QueryLoader constructor.
+     */
+    protected QueryLoader() {
+    }
+
+    /**
+     * Loads a Map of query names to SQL values.  The Maps are cached so a
+     * subsequent request to load queries from the same path will return
+     * the cached Map.  The properties file to load can be in either
+     * line-oriented or XML format.  XML formatted properties files must use a
+     * {@code .xml} file extension.
+     *
+     * @param path The path that the ClassLoader will use to find the file.
+     * This is <strong>not</strong> a file system path.  If you had a jarred
+     * Queries.properties file in the com.yourcorp.app.jdbc package you would
+     * pass "/com/yourcorp/app/jdbc/Queries.properties" to this method.
+     * @throws IOException if a file access error occurs
+     * @throws IllegalArgumentException if the ClassLoader can't find a file at
+     * the given path.
+     * @throws java.util.InvalidPropertiesFormatException if the XML properties file is
+     * invalid
+     * @return Map of query names to SQL values
+     * @see java.util.Properties
+     */
+    public synchronized Map<String, String> load(final String path) throws IOException {
+
+        Map<String, String> queryMap = this.queries.get(path);
+
+        if (queryMap == null) {
+            queryMap = this.loadQueries(path);
+            this.queries.put(path, queryMap);
+        }
+
+        return queryMap;
+    }
+
+    /**
+     * Loads a set of named queries into a Map object.  This implementation
+     * reads a properties file at the given path.  The properties file can be
+     * in either line-oriented or XML format.  XML formatted properties files
+     * must use a {@code .xml} file extension.
+
+     * @param path The path that the ClassLoader will use to find the file.
+     * @throws IOException if a file access error occurs
+     * @throws IllegalArgumentException if the ClassLoader can't find a file at
+     * the given path.
+     * @throws java.util.InvalidPropertiesFormatException if the XML properties file is
+     * invalid
+     * @since 1.1
+     * @return Map of query names to SQL values
+     * @see java.util.Properties
+     */
+    protected Map<String, String> loadQueries(final String path) throws IOException {
+        // Findbugs flags getClass().getResource as a bad practice; maybe we should change the API?
+        final Properties props;
+        try (InputStream in = getClass().getResourceAsStream(path)) {
+
+            if (in == null) {
+                throw new IllegalArgumentException(path + " not found.");
+            }
+            props = new Properties();
+            if (dotXml.matcher(path).matches()) {
+                props.loadFromXML(in);
+            } else {
+                props.load(in);
+            }
+        }
+
+        // Copy to HashMap for better performance
+
+        @SuppressWarnings({"rawtypes", "unchecked" }) // load() always creates <String,String> entries
+        final
+        HashMap<String, String> hashMap = new HashMap(props);
+        return hashMap;
+    }
+
+    /**
+     * Removes the queries for the given path from the cache.
+     * @param path The path that the queries were loaded from.
+     */
+    public synchronized void unload(final String path) {
+        this.queries.remove(path);
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/dbutils/QueryRunner.java b/src/main/java/org/apache/commons/dbutils/QueryRunner.java
index 6a1aee1..a55e7d0 100644
--- a/src/main/java/org/apache/commons/dbutils/QueryRunner.java
+++ b/src/main/java/org/apache/commons/dbutils/QueryRunner.java
@@ -125,7 +125,7 @@ public class QueryRunner extends AbstractQueryRunner {
      * this array is one set of batch replacement values.
      * @return The number of rows updated per statement.
      * @throws SQLException if a database access error occurs
-     * @since DbUtils 1.1
+     * @since 1.1
      */
     public int[] batch(final Connection conn, final String sql, final Object[][] params) throws SQLException {
         return this.batch(conn, false, sql, params);
@@ -142,7 +142,7 @@ public class QueryRunner extends AbstractQueryRunner {
      * this array is one set of batch replacement values.
      * @return The number of rows updated per statement.
      * @throws SQLException if a database access error occurs
-     * @since DbUtils 1.1
+     * @since 1.1
      */
     public int[] batch(final String sql, final Object[][] params) throws SQLException {
         final Connection conn = this.prepareConnection();
diff --git a/src/main/java/org/apache/commons/dbutils/ResultSetIterator.java b/src/main/java/org/apache/commons/dbutils/ResultSetIterator.java
index b96f39e..6f5a83c 100644
--- a/src/main/java/org/apache/commons/dbutils/ResultSetIterator.java
+++ b/src/main/java/org/apache/commons/dbutils/ResultSetIterator.java
@@ -1,141 +1,141 @@
-/*
- * 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.ResultSet;
-import java.sql.SQLException;
-import java.util.Iterator;
-
-/**
- * <p>
- * Wraps a {@code ResultSet} in an {@code Iterator&lt;Object[]&gt;}.  This is useful
- * when you want to present a non-database application layer with domain
- * neutral data.
- * </p>
- *
- * <p>
- * This implementation requires the {@code ResultSet.isLast()} method
- * to be implemented.
- * </p>
- */
-public class ResultSetIterator implements Iterator<Object[]> {
-
-    /**
-     * The wrapped {@code ResultSet}.
-     */
-    private final ResultSet rs;
-
-    /**
-     * The processor to use when converting a row into an Object[].
-     */
-    private final RowProcessor convert;
-
-    /**
-     * Constructor for ResultSetIterator.
-     * @param rs Wrap this {@code ResultSet} in an {@code Iterator}.
-     */
-    public ResultSetIterator(final ResultSet rs) {
-        this(rs, new BasicRowProcessor());
-    }
-
-    /**
-     * Constructor for ResultSetIterator.
-     * @param rs Wrap this {@code ResultSet} in an {@code Iterator}.
-     * @param convert The processor to use when converting a row into an
-     * {@code Object[]}.  Defaults to a
-     * {@code BasicRowProcessor}.
-     */
-    public ResultSetIterator(final ResultSet rs, final RowProcessor convert) {
-        this.rs = rs;
-        this.convert = convert;
-    }
-
-    /**
-     * Returns true if there are more rows in the ResultSet.
-     * @return boolean {@code true} if there are more rows
-     * @throws RuntimeException if an SQLException occurs.
-     */
-    @Override
-    public boolean hasNext() {
-        try {
-            return !rs.isLast();
-        } catch (final SQLException e) {
-            rethrow(e);
-            return false;
-        }
-    }
-
-    /**
-     * Returns the next row as an {@code Object[]}.
-     * @return An {@code Object[]} with the same number of elements as
-     * columns in the {@code ResultSet}.
-     * @see java.util.Iterator#next()
-     * @throws RuntimeException if an SQLException occurs.
-     */
-    @Override
-    public Object[] next() {
-        try {
-            rs.next();
-            return this.convert.toArray(rs);
-        } catch (final SQLException e) {
-            rethrow(e);
-            return null;
-        }
-    }
-
-    /**
-     * Deletes the current row from the {@code ResultSet}.
-     * @see java.util.Iterator#remove()
-     * @throws RuntimeException if an SQLException occurs.
-     */
-    @Override
-    public void remove() {
-        try {
-            this.rs.deleteRow();
-        } catch (final SQLException e) {
-            rethrow(e);
-        }
-    }
-
-    /**
-     * Rethrow the SQLException as a RuntimeException.  This implementation
-     * creates a new RuntimeException with the SQLException's error message.
-     * @param e SQLException to rethrow
-     * @since DbUtils 1.1
-     */
-    protected void rethrow(final SQLException e) {
-        throw new RuntimeException(e.getMessage());
-    }
-
-    /**
-     * Generates an {@code Iterable}, suitable for use in for-each loops.
-     *
-     * @param rs Wrap this {@code ResultSet} in an {@code Iterator}.
-     * @return an {@code Iterable}, suitable for use in for-each loops.
-     */
-    public static Iterable<Object[]> iterable(final ResultSet rs) {
-        return new Iterable<Object[]>() {
-
-            @Override
-            public Iterator<Object[]> iterator() {
-                return new ResultSetIterator(rs);
-            }
-
-        };
-    }
-
-}
+/*
+ * 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.ResultSet;
+import java.sql.SQLException;
+import java.util.Iterator;
+
+/**
+ * <p>
+ * Wraps a {@code ResultSet} in an {@code Iterator&lt;Object[]&gt;}.  This is useful
+ * when you want to present a non-database application layer with domain
+ * neutral data.
+ * </p>
+ *
+ * <p>
+ * This implementation requires the {@code ResultSet.isLast()} method
+ * to be implemented.
+ * </p>
+ */
+public class ResultSetIterator implements Iterator<Object[]> {
+
+    /**
+     * The wrapped {@code ResultSet}.
+     */
+    private final ResultSet rs;
+
+    /**
+     * The processor to use when converting a row into an Object[].
+     */
+    private final RowProcessor convert;
+
+    /**
+     * Constructor for ResultSetIterator.
+     * @param rs Wrap this {@code ResultSet} in an {@code Iterator}.
+     */
+    public ResultSetIterator(final ResultSet rs) {
+        this(rs, new BasicRowProcessor());
+    }
+
+    /**
+     * Constructor for ResultSetIterator.
+     * @param rs Wrap this {@code ResultSet} in an {@code Iterator}.
+     * @param convert The processor to use when converting a row into an
+     * {@code Object[]}.  Defaults to a
+     * {@code BasicRowProcessor}.
+     */
+    public ResultSetIterator(final ResultSet rs, final RowProcessor convert) {
+        this.rs = rs;
+        this.convert = convert;
+    }
+
+    /**
+     * Returns true if there are more rows in the ResultSet.
+     * @return boolean {@code true} if there are more rows
+     * @throws RuntimeException if an SQLException occurs.
+     */
+    @Override
+    public boolean hasNext() {
+        try {
+            return !rs.isLast();
+        } catch (final SQLException e) {
+            rethrow(e);
+            return false;
+        }
+    }
+
+    /**
+     * Returns the next row as an {@code Object[]}.
+     * @return An {@code Object[]} with the same number of elements as
+     * columns in the {@code ResultSet}.
+     * @see java.util.Iterator#next()
+     * @throws RuntimeException if an SQLException occurs.
+     */
+    @Override
+    public Object[] next() {
+        try {
+            rs.next();
+            return this.convert.toArray(rs);
+        } catch (final SQLException e) {
+            rethrow(e);
+            return null;
+        }
+    }
+
+    /**
+     * Deletes the current row from the {@code ResultSet}.
+     * @see java.util.Iterator#remove()
+     * @throws RuntimeException if an SQLException occurs.
+     */
+    @Override
+    public void remove() {
+        try {
+            this.rs.deleteRow();
+        } catch (final SQLException e) {
+            rethrow(e);
+        }
+    }
+
+    /**
+     * Rethrow the SQLException as a RuntimeException.  This implementation
+     * creates a new RuntimeException with the SQLException's error message.
+     * @param e SQLException to rethrow
+     * @since 1.1
+     */
+    protected void rethrow(final SQLException e) {
+        throw new RuntimeException(e.getMessage());
+    }
+
+    /**
+     * Generates an {@code Iterable}, suitable for use in for-each loops.
+     *
+     * @param rs Wrap this {@code ResultSet} in an {@code Iterator}.
+     * @return an {@code Iterable}, suitable for use in for-each loops.
+     */
+    public static Iterable<Object[]> iterable(final ResultSet rs) {
+        return new Iterable<Object[]>() {
+
+            @Override
+            public Iterator<Object[]> iterator() {
+                return new ResultSetIterator(rs);
+            }
+
+        };
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/AbstractKeyedHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/AbstractKeyedHandler.java
index 0432bb1..e2a692d 100644
--- a/src/main/java/org/apache/commons/dbutils/handlers/AbstractKeyedHandler.java
+++ b/src/main/java/org/apache/commons/dbutils/handlers/AbstractKeyedHandler.java
@@ -1,87 +1,87 @@
-/*
- * 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.handlers;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.commons.dbutils.ResultSetHandler;
-
-/**
- * <p>
- * {@code ResultSetHandler} implementation that returns a Map.
- * {@code ResultSet} rows are converted into objects (Vs) which are then stored
- * in a Map under the given keys (Ks).
- * </p>
- *
- * @param <K> the type of keys maintained by the returned map
- * @param <V> the type of mapped values
- * @see org.apache.commons.dbutils.ResultSetHandler
- * @since DbUtils 1.3
- */
-public abstract class AbstractKeyedHandler<K, V> implements ResultSetHandler<Map<K, V>> {
-
-
-    /**
-     * Convert each row's columns into a Map and store then
-     * in a {@code Map} under {@code ResultSet.getObject(key)} key.
-     * @param rs {@code ResultSet} to process.
-     * @return A {@code Map}, never {@code null}.
-     * @throws SQLException if a database access error occurs
-     * @see org.apache.commons.dbutils.ResultSetHandler#handle(java.sql.ResultSet)
-     */
-    @Override
-    public Map<K, V> handle(final ResultSet rs) throws SQLException {
-        final Map<K, V> result = createMap();
-        while (rs.next()) {
-            result.put(createKey(rs), createRow(rs));
-        }
-        return result;
-    }
-
-    /**
-     * This factory method is called by {@code handle()} to create the Map
-     * to store records in.  This implementation returns a {@code HashMap}
-     * instance.
-     *
-     * @return Map to store records in
-     */
-    protected Map<K, V> createMap() {
-        return new HashMap<>();
-    }
-
-    /**
-     * This factory method is called by {@code handle()} to retrieve the
-     * key value from the current {@code ResultSet} row.
-     * @param rs ResultSet to create a key from
-     * @return K from the configured key column name/index
-     * @throws SQLException if a database access error occurs
-     */
-    protected abstract K createKey(ResultSet rs) throws SQLException;
-
-    /**
-     * This factory method is called by {@code handle()} to store the
-     * current {@code ResultSet} row in some object.
-     * @param rs ResultSet to create a row from
-     * @return V object created from the current row
-     * @throws SQLException if a database access error occurs
-     */
-    protected abstract V createRow(ResultSet rs) throws SQLException;
-
-}
+/*
+ * 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.handlers;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.dbutils.ResultSetHandler;
+
+/**
+ * <p>
+ * {@code ResultSetHandler} implementation that returns a Map.
+ * {@code ResultSet} rows are converted into objects (Vs) which are then stored
+ * in a Map under the given keys (Ks).
+ * </p>
+ *
+ * @param <K> the type of keys maintained by the returned map
+ * @param <V> the type of mapped values
+ * @see org.apache.commons.dbutils.ResultSetHandler
+ * @since 1.3
+ */
+public abstract class AbstractKeyedHandler<K, V> implements ResultSetHandler<Map<K, V>> {
+
+
+    /**
+     * Convert each row's columns into a Map and store then
+     * in a {@code Map} under {@code ResultSet.getObject(key)} key.
+     * @param rs {@code ResultSet} to process.
+     * @return A {@code Map}, never {@code null}.
+     * @throws SQLException if a database access error occurs
+     * @see org.apache.commons.dbutils.ResultSetHandler#handle(java.sql.ResultSet)
+     */
+    @Override
+    public Map<K, V> handle(final ResultSet rs) throws SQLException {
+        final Map<K, V> result = createMap();
+        while (rs.next()) {
+            result.put(createKey(rs), createRow(rs));
+        }
+        return result;
+    }
+
+    /**
+     * This factory method is called by {@code handle()} to create the Map
+     * to store records in.  This implementation returns a {@code HashMap}
+     * instance.
+     *
+     * @return Map to store records in
+     */
+    protected Map<K, V> createMap() {
+        return new HashMap<>();
+    }
+
+    /**
+     * This factory method is called by {@code handle()} to retrieve the
+     * key value from the current {@code ResultSet} row.
+     * @param rs ResultSet to create a key from
+     * @return K from the configured key column name/index
+     * @throws SQLException if a database access error occurs
+     */
+    protected abstract K createKey(ResultSet rs) throws SQLException;
+
+    /**
+     * This factory method is called by {@code handle()} to store the
+     * current {@code ResultSet} row in some object.
+     * @param rs ResultSet to create a row from
+     * @return V object created from the current row
+     * @throws SQLException if a database access error occurs
+     */
+    protected abstract V createRow(ResultSet rs) throws SQLException;
+
+}
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/BeanMapHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/BeanMapHandler.java
index e50d239..420300e 100644
--- a/src/main/java/org/apache/commons/dbutils/handlers/BeanMapHandler.java
+++ b/src/main/java/org/apache/commons/dbutils/handlers/BeanMapHandler.java
@@ -1,184 +1,184 @@
-/*
- * 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.handlers;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-
-import org.apache.commons.dbutils.RowProcessor;
-
-/**
- * <p>
- * {@code ResultSetHandler} implementation that returns a Map of Beans.
- * {@code ResultSet} rows are converted into Beans which are then stored in
- * a Map under the given key.
- * </p>
- * <p>
- * If you had a Person table with a primary key column called ID, you could
- * retrieve rows from the table like this:
- *
- * <pre>
- * ResultSetHandler&lt;Map&lt;Long, Person&gt;&gt; h = new BeanMapHandler&lt;Long, Person&gt;(Person.class, &quot;id&quot;);
- * Map&lt;Long, Person&gt; found = queryRunner.query(&quot;select id, name, age from person&quot;, h);
- * Person jane = found.get(1L); // jane's id is 1
- * String janesName = jane.getName();
- * Integer janesAge = jane.getAge();
- * </pre>
- *
- * Note that the "id" passed to BeanMapHandler can be in any case. The data type
- * returned for id is dependent upon how your JDBC driver converts SQL column
- * types from the Person table into Java types. The "name" and "age" columns are
- * converted according to their property descriptors by DbUtils.
- * &lt;/p&gt;
- * <p>
- * This class is thread safe.
- * &lt;/p&gt;
- *
- * @param <K>
- *            the type of keys maintained by the returned map
- * @param <V>
- *            the type of the bean
- * @see org.apache.commons.dbutils.ResultSetHandler
- * @since DbUtils 1.5
- */
-public class BeanMapHandler<K, V> extends AbstractKeyedHandler<K, V> {
-
-    /**
-     * The Class of beans produced by this handler.
-     */
-    private final Class<V> type;
-
-    /**
-     * The RowProcessor implementation to use when converting rows into Objects.
-     */
-    private final RowProcessor convert;
-
-    /**
-     * The column index to retrieve key values from. Defaults to 1.
-     */
-    private final int columnIndex;
-
-    /**
-     * The column name to retrieve key values from. Either columnName or
-     * columnIndex will be used but never both.
-     */
-    private final String columnName;
-
-    /**
-     * Creates a new instance of BeanMapHandler. The value of the first column
-     * of each row will be a key in the Map.
-     *
-     * @param type
-     *            The Class that objects returned from {@code createRow()}
-     *            are created from.
-     */
-    public BeanMapHandler(final Class<V> type) {
-        this(type, ArrayHandler.ROW_PROCESSOR, 1, null);
-    }
-
-    /**
-     * Creates a new instance of BeanMapHandler. The value of the first column
-     * of each row will be a key in the Map.
-     *
-     * @param type
-     *            The Class that objects returned from {@code createRow()}
-     *            are created from.
-     * @param convert
-     *            The {@code RowProcessor} implementation to use when
-     *            converting rows into Beans
-     */
-    public BeanMapHandler(final Class<V> type, final RowProcessor convert) {
-        this(type, convert, 1, null);
-    }
-
-    /**
-     * Creates a new instance of BeanMapHandler.
-     *
-     * @param type
-     *            The Class that objects returned from {@code createRow()}
-     *            are created from.
-     * @param columnIndex
-     *            The values to use as keys in the Map are retrieved from the
-     *            column at this index.
-     */
-    public BeanMapHandler(final Class<V> type, final int columnIndex) {
-        this(type, ArrayHandler.ROW_PROCESSOR, columnIndex, null);
-    }
-
-    /**
-     * Creates a new instance of BeanMapHandler.
-     *
-     * @param type
-     *            The Class that objects returned from {@code createRow()}
-     *            are created from.
-     * @param columnName
-     *            The values to use as keys in the Map are retrieved from the
-     *            column with this name.
-     */
-    public BeanMapHandler(final Class<V> type, final String columnName) {
-        this(type, ArrayHandler.ROW_PROCESSOR, 1, columnName);
-    }
-
-    /**
-     * Private Helper
-     *
-     * @param convert
-     *            The {@code RowProcessor} implementation to use when
-     *            converting rows into Beans
-     * @param columnIndex
-     *            The values to use as keys in the Map are retrieved from the
-     *            column at this index.
-     * @param columnName
-     *            The values to use as keys in the Map are retrieved from the
-     *            column with this name.
-     */
-    private BeanMapHandler(final Class<V> type, final RowProcessor convert,
-            final int columnIndex, final String columnName) {
-        this.type = type;
-        this.convert = convert;
-        this.columnIndex = columnIndex;
-        this.columnName = columnName;
-    }
-
-    /**
-     * This factory method is called by {@code handle()} to retrieve the
-     * key value from the current {@code ResultSet} row.
-     * @param rs ResultSet to create a key from
-     *
-     * @return K from the configured key column name/index
-     *
-     * @throws SQLException if a database access error occurs
-     * @throws ClassCastException if the class datatype does not match the column type
-     *
-     * @see org.apache.commons.dbutils.handlers.AbstractKeyedHandler#createKey(ResultSet)
-     */
-    // We assume that the user has picked the correct type to match the column
-    // so getObject will return the appropriate type and the cast will succeed.
-    @SuppressWarnings("unchecked")
-    @Override
-    protected K createKey(final ResultSet rs) throws SQLException {
-        return (columnName == null) ?
-               (K) rs.getObject(columnIndex) :
-               (K) rs.getObject(columnName);
-    }
-
-    @Override
-    protected V createRow(final ResultSet rs) throws SQLException {
-        return this.convert.toBean(rs, type);
-    }
-
-}
+/*
+ * 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.handlers;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.dbutils.RowProcessor;
+
+/**
+ * <p>
+ * {@code ResultSetHandler} implementation that returns a Map of Beans.
+ * {@code ResultSet} rows are converted into Beans which are then stored in
+ * a Map under the given key.
+ * </p>
+ * <p>
+ * If you had a Person table with a primary key column called ID, you could
+ * retrieve rows from the table like this:
+ *
+ * <pre>
+ * ResultSetHandler&lt;Map&lt;Long, Person&gt;&gt; h = new BeanMapHandler&lt;Long, Person&gt;(Person.class, &quot;id&quot;);
+ * Map&lt;Long, Person&gt; found = queryRunner.query(&quot;select id, name, age from person&quot;, h);
+ * Person jane = found.get(1L); // jane's id is 1
+ * String janesName = jane.getName();
+ * Integer janesAge = jane.getAge();
+ * </pre>
+ *
+ * Note that the "id" passed to BeanMapHandler can be in any case. The data type
+ * returned for id is dependent upon how your JDBC driver converts SQL column
+ * types from the Person table into Java types. The "name" and "age" columns are
+ * converted according to their property descriptors by DbUtils.
+ * &lt;/p&gt;
+ * <p>
+ * This class is thread safe.
+ * &lt;/p&gt;
+ *
+ * @param <K>
+ *            the type of keys maintained by the returned map
+ * @param <V>
+ *            the type of the bean
+ * @see org.apache.commons.dbutils.ResultSetHandler
+ * @since 1.5
+ */
+public class BeanMapHandler<K, V> extends AbstractKeyedHandler<K, V> {
+
+    /**
+     * The Class of beans produced by this handler.
+     */
+    private final Class<V> type;
+
+    /**
+     * The RowProcessor implementation to use when converting rows into Objects.
+     */
+    private final RowProcessor convert;
+
+    /**
+     * The column index to retrieve key values from. Defaults to 1.
+     */
+    private final int columnIndex;
+
+    /**
+     * The column name to retrieve key values from. Either columnName or
+     * columnIndex will be used but never both.
+     */
+    private final String columnName;
+
+    /**
+     * Creates a new instance of BeanMapHandler. The value of the first column
+     * of each row will be a key in the Map.
+     *
+     * @param type
+     *            The Class that objects returned from {@code createRow()}
+     *            are created from.
+     */
+    public BeanMapHandler(final Class<V> type) {
+        this(type, ArrayHandler.ROW_PROCESSOR, 1, null);
+    }
+
+    /**
+     * Creates a new instance of BeanMapHandler. The value of the first column
+     * of each row will be a key in the Map.
+     *
+     * @param type
+     *            The Class that objects returned from {@code createRow()}
+     *            are created from.
+     * @param convert
+     *            The {@code RowProcessor} implementation to use when
+     *            converting rows into Beans
+     */
+    public BeanMapHandler(final Class<V> type, final RowProcessor convert) {
+        this(type, convert, 1, null);
+    }
+
+    /**
+     * Creates a new instance of BeanMapHandler.
+     *
+     * @param type
+     *            The Class that objects returned from {@code createRow()}
+     *            are created from.
+     * @param columnIndex
+     *            The values to use as keys in the Map are retrieved from the
+     *            column at this index.
+     */
+    public BeanMapHandler(final Class<V> type, final int columnIndex) {
+        this(type, ArrayHandler.ROW_PROCESSOR, columnIndex, null);
+    }
+
+    /**
+     * Creates a new instance of BeanMapHandler.
+     *
+     * @param type
+     *            The Class that objects returned from {@code createRow()}
+     *            are created from.
+     * @param columnName
+     *            The values to use as keys in the Map are retrieved from the
+     *            column with this name.
+     */
+    public BeanMapHandler(final Class<V> type, final String columnName) {
+        this(type, ArrayHandler.ROW_PROCESSOR, 1, columnName);
+    }
+
+    /**
+     * Private Helper
+     *
+     * @param convert
+     *            The {@code RowProcessor} implementation to use when
+     *            converting rows into Beans
+     * @param columnIndex
+     *            The values to use as keys in the Map are retrieved from the
+     *            column at this index.
+     * @param columnName
+     *            The values to use as keys in the Map are retrieved from the
+     *            column with this name.
+     */
+    private BeanMapHandler(final Class<V> type, final RowProcessor convert,
+            final int columnIndex, final String columnName) {
+        this.type = type;
+        this.convert = convert;
+        this.columnIndex = columnIndex;
+        this.columnName = columnName;
+    }
+
+    /**
+     * This factory method is called by {@code handle()} to retrieve the
+     * key value from the current {@code ResultSet} row.
+     * @param rs ResultSet to create a key from
+     *
+     * @return K from the configured key column name/index
+     *
+     * @throws SQLException if a database access error occurs
+     * @throws ClassCastException if the class datatype does not match the column type
+     *
+     * @see org.apache.commons.dbutils.handlers.AbstractKeyedHandler#createKey(ResultSet)
+     */
+    // We assume that the user has picked the correct type to match the column
+    // so getObject will return the appropriate type and the cast will succeed.
+    @SuppressWarnings("unchecked")
+    @Override
+    protected K createKey(final ResultSet rs) throws SQLException {
+        return (columnName == null) ?
+               (K) rs.getObject(columnIndex) :
+               (K) rs.getObject(columnName);
+    }
+
+    @Override
+    protected V createRow(final ResultSet rs) throws SQLException {
+        return this.convert.toBean(rs, type);
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/ColumnListHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/ColumnListHandler.java
index 0b3ba52..00b025e 100644
--- a/src/main/java/org/apache/commons/dbutils/handlers/ColumnListHandler.java
+++ b/src/main/java/org/apache/commons/dbutils/handlers/ColumnListHandler.java
@@ -1,104 +1,104 @@
-/*
- * 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.handlers;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-
-/**
- * {@code ResultSetHandler} implementation that converts one
- * {@code ResultSet} column into a {@code List} of
- * {@code Object}s. This class is thread safe.
- *
- * @param <T> The type of the column.
- * @see org.apache.commons.dbutils.ResultSetHandler
- * @since DbUtils 1.1
- */
-public class ColumnListHandler<T> extends AbstractListHandler<T> {
-
-    /**
-     * The column number to retrieve.
-     */
-    private final int columnIndex;
-
-    /**
-     * The column name to retrieve.  Either columnName or columnIndex
-     * will be used but never both.
-     */
-    private final String columnName;
-
-    /**
-     * Creates a new instance of ColumnListHandler.  The first column of each
-     * row will be returned from {@code handle()}.
-     */
-    public ColumnListHandler() {
-        this(1, null);
-    }
-
-    /**
-     * Creates a new instance of ColumnListHandler.
-     *
-     * @param columnIndex The index of the column to retrieve from the
-     * {@code ResultSet}.
-     */
-    public ColumnListHandler(final int columnIndex) {
-        this(columnIndex, null);
-    }
-
-    /**
-     * Creates a new instance of ColumnListHandler.
-     *
-     * @param columnName The name of the column to retrieve from the
-     * {@code ResultSet}.
-     */
-    public ColumnListHandler(final String columnName) {
-        this(1, columnName);
-    }
-
-    /** Private Helper
-     * @param columnIndex The index of the column to retrieve from the
-     * {@code ResultSet}.
-     * @param columnName The name of the column to retrieve from the
-     * {@code ResultSet}.
-     */
-    private ColumnListHandler(final int columnIndex, final String columnName) {
-        this.columnIndex = columnIndex;
-        this.columnName = columnName;
-    }
-
-    /**
-     * Returns one {@code ResultSet} column value as {@code Object}.
-     * @param rs {@code ResultSet} to process.
-     * @return {@code Object}, never {@code null}.
-     *
-     * @throws SQLException if a database access error occurs
-     * @throws ClassCastException if the class datatype does not match the column type
-     *
-     * @see org.apache.commons.dbutils.handlers.AbstractListHandler#handle(ResultSet)
-     */
-    // We assume that the user has picked the correct type to match the column
-    // so getObject will return the appropriate type and the cast will succeed.
-    @SuppressWarnings("unchecked")
-    @Override
-    protected T handleRow(final ResultSet rs) throws SQLException {
-        if (this.columnName == null) {
-            return (T) rs.getObject(this.columnIndex);
-        }
-        return (T) rs.getObject(this.columnName);
-   }
-
-}
+/*
+ * 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.handlers;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * {@code ResultSetHandler} implementation that converts one
+ * {@code ResultSet} column into a {@code List} of
+ * {@code Object}s. This class is thread safe.
+ *
+ * @param <T> The type of the column.
+ * @see org.apache.commons.dbutils.ResultSetHandler
+ * @since 1.1
+ */
+public class ColumnListHandler<T> extends AbstractListHandler<T> {
+
+    /**
+     * The column number to retrieve.
+     */
+    private final int columnIndex;
+
+    /**
+     * The column name to retrieve.  Either columnName or columnIndex
+     * will be used but never both.
+     */
+    private final String columnName;
+
+    /**
+     * Creates a new instance of ColumnListHandler.  The first column of each
+     * row will be returned from {@code handle()}.
+     */
+    public ColumnListHandler() {
+        this(1, null);
+    }
+
+    /**
+     * Creates a new instance of ColumnListHandler.
+     *
+     * @param columnIndex The index of the column to retrieve from the
+     * {@code ResultSet}.
+     */
+    public ColumnListHandler(final int columnIndex) {
+        this(columnIndex, null);
+    }
+
+    /**
+     * Creates a new instance of ColumnListHandler.
+     *
+     * @param columnName The name of the column to retrieve from the
+     * {@code ResultSet}.
+     */
+    public ColumnListHandler(final String columnName) {
+        this(1, columnName);
+    }
+
+    /** Private Helper
+     * @param columnIndex The index of the column to retrieve from the
+     * {@code ResultSet}.
+     * @param columnName The name of the column to retrieve from the
+     * {@code ResultSet}.
+     */
+    private ColumnListHandler(final int columnIndex, final String columnName) {
+        this.columnIndex = columnIndex;
+        this.columnName = columnName;
+    }
+
+    /**
+     * Returns one {@code ResultSet} column value as {@code Object}.
+     * @param rs {@code ResultSet} to process.
+     * @return {@code Object}, never {@code null}.
+     *
+     * @throws SQLException if a database access error occurs
+     * @throws ClassCastException if the class datatype does not match the column type
+     *
+     * @see org.apache.commons.dbutils.handlers.AbstractListHandler#handle(ResultSet)
+     */
+    // We assume that the user has picked the correct type to match the column
+    // so getObject will return the appropriate type and the cast will succeed.
+    @SuppressWarnings("unchecked")
+    @Override
+    protected T handleRow(final ResultSet rs) throws SQLException {
+        if (this.columnName == null) {
+            return (T) rs.getObject(this.columnIndex);
+        }
+        return (T) rs.getObject(this.columnName);
+   }
+
+}
diff --git a/src/main/java/org/apache/commons/dbutils/handlers/KeyedHandler.java b/src/main/java/org/apache/commons/dbutils/handlers/KeyedHandler.java
index a2baa05..829f053 100644
--- a/src/main/java/org/apache/commons/dbutils/handlers/KeyedHandler.java
+++ b/src/main/java/org/apache/commons/dbutils/handlers/KeyedHandler.java
@@ -1,160 +1,160 @@
-/*
- * 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.handlers;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.Map;
-
-import org.apache.commons.dbutils.RowProcessor;
-
-/**
- * <p>
- * {@code ResultSetHandler} implementation that returns a Map of Maps.
- * {@code ResultSet} rows are converted into Maps which are then stored
- * in a Map under the given key.
- * </p>
- * <p>
- * If you had a Person table with a primary key column called ID, you could
- * retrieve rows from the table like this:
- * <pre>
- * ResultSetHandler h = new KeyedHandler("id");
- * Map found = (Map) queryRunner.query("select id, name, age from person", h);
- * Map jane = (Map) found.get(new Long(1)); // jane's id is 1
- * String janesName = (String) jane.get("name");
- * Integer janesAge = (Integer) jane.get("age");
- * </pre>
- * Note that the "id" passed to KeyedHandler and "name" and "age" passed to the
- * returned Map's get() method can be in any case.  The data types returned for
- * name and age are dependent upon how your JDBC driver converts SQL column
- * types from the Person table into Java types.
- * &lt;/p&gt;
- * <p>This class is thread safe.</p>
- *
- * @param <K> The type of the key
- * @see org.apache.commons.dbutils.ResultSetHandler
- * @since DbUtils 1.1
- */
-public class KeyedHandler<K> extends AbstractKeyedHandler<K, Map<String, Object>> {
-
-    /**
-     * The RowProcessor implementation to use when converting rows
-     * into Objects.
-     */
-    protected final RowProcessor convert;
-
-    /**
-     * The column index to retrieve key values from.  Defaults to 1.
-     */
-    protected final int columnIndex;
-
-    /**
-     * The column name to retrieve key values from.  Either columnName or
-     * columnIndex will be used but never both.
-     */
-    protected final String columnName;
-
-    /**
-     * Creates a new instance of KeyedHandler.  The value of the first column
-     * of each row will be a key in the Map.
-     */
-    public KeyedHandler() {
-        this(ArrayHandler.ROW_PROCESSOR, 1, null);
-    }
-
-    /**
-     * Creates a new instance of KeyedHandler.  The value of the first column
-     * of each row will be a key in the Map.
-     *
-     * @param convert The {@code RowProcessor} implementation
-     * to use when converting rows into Maps
-     */
-    public KeyedHandler(final RowProcessor convert) {
-        this(convert, 1, null);
-    }
-
-    /**
-     * Creates a new instance of KeyedHandler.
-     *
-     * @param columnIndex The values to use as keys in the Map are
-     * retrieved from the column at this index.
-     */
-    public KeyedHandler(final int columnIndex) {
-        this(ArrayHandler.ROW_PROCESSOR, columnIndex, null);
-    }
-
-    /**
-     * Creates a new instance of KeyedHandler.
-     *
-     * @param columnName The values to use as keys in the Map are
-     * retrieved from the column with this name.
-     */
-    public KeyedHandler(final String columnName) {
-        this(ArrayHandler.ROW_PROCESSOR, 1, columnName);
-    }
-
-    /** Private Helper
-     * @param convert The {@code RowProcessor} implementation
-     * to use when converting rows into Maps
-     * @param columnIndex The values to use as keys in the Map are
-     * retrieved from the column at this index.
-     * @param columnName The values to use as keys in the Map are
-     * retrieved from the column with this name.
-     */
-    private KeyedHandler(final RowProcessor convert, final int columnIndex,
-            final String columnName) {
-        this.convert = convert;
-        this.columnIndex = columnIndex;
-        this.columnName = columnName;
-    }
-    /**
-     * This factory method is called by {@code handle()} to retrieve the
-     * key value from the current {@code ResultSet} row.  This
-     * implementation returns {@code ResultSet.getObject()} for the
-     * configured key column name or index.
-     * @param rs ResultSet to create a key from
-     * @return Object from the configured key column name/index
-     *
-     * @throws SQLException if a database access error occurs
-     * @throws ClassCastException if the class datatype does not match the column type
-     */
-    // We assume that the user has picked the correct type to match the column
-    // so getObject will return the appropriate type and the cast will succeed.
-    @SuppressWarnings("unchecked")
-    @Override
-    protected K createKey(final ResultSet rs) throws SQLException {
-        return (columnName == null) ?
-               (K) rs.getObject(columnIndex) :
-               (K) rs.getObject(columnName);
-    }
-
-    /**
-     * This factory method is called by {@code handle()} to store the
-     * current {@code ResultSet} row in some object. This
-     * implementation returns a {@code Map} with case insensitive column
-     * names as keys.  Calls to {@code map.get("COL")} and
-     * {@code map.get("col")} return the same value.
-     * @param rs ResultSet to create a row from
-     * @return Object typed Map containing column names to values
-     * @throws SQLException if a database access error occurs
-     */
-    @Override
-    protected Map<String, Object> createRow(final ResultSet rs) throws SQLException {
-        return this.convert.toMap(rs);
-    }
-
-}
+/*
+ * 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.handlers;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.apache.commons.dbutils.RowProcessor;
+
+/**
+ * <p>
+ * {@code ResultSetHandler} implementation that returns a Map of Maps.
+ * {@code ResultSet} rows are converted into Maps which are then stored
+ * in a Map under the given key.
+ * </p>
+ * <p>
+ * If you had a Person table with a primary key column called ID, you could
+ * retrieve rows from the table like this:
+ * <pre>
+ * ResultSetHandler h = new KeyedHandler("id");
+ * Map found = (Map) queryRunner.query("select id, name, age from person", h);
+ * Map jane = (Map) found.get(new Long(1)); // jane's id is 1
+ * String janesName = (String) jane.get("name");
+ * Integer janesAge = (Integer) jane.get("age");
+ * </pre>
+ * Note that the "id" passed to KeyedHandler and "name" and "age" passed to the
+ * returned Map's get() method can be in any case.  The data types returned for
+ * name and age are dependent upon how your JDBC driver converts SQL column
+ * types from the Person table into Java types.
+ * &lt;/p&gt;
+ * <p>This class is thread safe.</p>
+ *
+ * @param <K> The type of the key
+ * @see org.apache.commons.dbutils.ResultSetHandler
+ * @since 1.1
+ */
+public class KeyedHandler<K> extends AbstractKeyedHandler<K, Map<String, Object>> {
+
+    /**
+     * The RowProcessor implementation to use when converting rows
+     * into Objects.
+     */
+    protected final RowProcessor convert;
+
+    /**
+     * The column index to retrieve key values from.  Defaults to 1.
+     */
+    protected final int columnIndex;
+
+    /**
+     * The column name to retrieve key values from.  Either columnName or
+     * columnIndex will be used but never both.
+     */
+    protected final String columnName;
+
+    /**
+     * Creates a new instance of KeyedHandler.  The value of the first column
+     * of each row will be a key in the Map.
+     */
+    public KeyedHandler() {
+        this(ArrayHandler.ROW_PROCESSOR, 1, null);
+    }
+
+    /**
+     * Creates a new instance of KeyedHandler.  The value of the first column
+     * of each row will be a key in the Map.
+     *
+     * @param convert The {@code RowProcessor} implementation
+     * to use when converting rows into Maps
+     */
+    public KeyedHandler(final RowProcessor convert) {
+        this(convert, 1, null);
+    }
+
+    /**
+     * Creates a new instance of KeyedHandler.
+     *
+     * @param columnIndex The values to use as keys in the Map are
+     * retrieved from the column at this index.
+     */
+    public KeyedHandler(final int columnIndex) {
+        this(ArrayHandler.ROW_PROCESSOR, columnIndex, null);
+    }
+
+    /**
+     * Creates a new instance of KeyedHandler.
+     *
+     * @param columnName The values to use as keys in the Map are
+     * retrieved from the column with this name.
+     */
+    public KeyedHandler(final String columnName) {
+        this(ArrayHandler.ROW_PROCESSOR, 1, columnName);
+    }
+
+    /** Private Helper
+     * @param convert The {@code RowProcessor} implementation
+     * to use when converting rows into Maps
+     * @param columnIndex The values to use as keys in the Map are
+     * retrieved from the column at this index.
+     * @param columnName The values to use as keys in the Map are
+     * retrieved from the column with this name.
+     */
+    private KeyedHandler(final RowProcessor convert, final int columnIndex,
+            final String columnName) {
+        this.convert = convert;
+        this.columnIndex = columnIndex;
+        this.columnName = columnName;
+    }
+    /**
+     * This factory method is called by {@code handle()} to retrieve the
+     * key value from the current {@code ResultSet} row.  This
+     * implementation returns {@code ResultSet.getObject()} for the
+     * configured key column name or index.
+     * @param rs ResultSet to create a key from
+     * @return Object from the configured key column name/index
+     *
+     * @throws SQLException if a database access error occurs
+     * @throws ClassCastException if the class datatype does not match the column type
+     */
+    // We assume that the user has picked the correct type to match the column
+    // so getObject will return the appropriate type and the cast will succeed.
+    @SuppressWarnings("unchecked")
+    @Override
+    protected K createKey(final ResultSet rs) throws SQLException {
+        return (columnName == null) ?
+               (K) rs.getObject(columnIndex) :
+               (K) rs.getObject(columnName);
+    }
+
+    /**
+     * This factory method is called by {@code handle()} to store the
+     * current {@code ResultSet} row in some object. This
+     * implementation returns a {@code Map} with case insensitive column
+     * names as keys.  Calls to {@code map.get("COL")} and
+     * {@code map.get("col")} return the same value.
+     * @param rs ResultSet to create a row from
+     * @return Object typed Map containing column names to values
+     * @throws SQLException if a database access error occurs
+     */
+    @Override
+    protected Map<String, Object> createRow(final ResultSet rs) throws SQLException {
+        return this.convert.toMap(rs);
+    }
+
+}