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 2018/05/04 16:14:22 UTC

commons-dbutils git commit: Sort methods in AB order.

Repository: commons-dbutils
Updated Branches:
  refs/heads/master 8d41daf40 -> e672a7b52


Sort methods in AB order.

Project: http://git-wip-us.apache.org/repos/asf/commons-dbutils/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-dbutils/commit/e672a7b5
Tree: http://git-wip-us.apache.org/repos/asf/commons-dbutils/tree/e672a7b5
Diff: http://git-wip-us.apache.org/repos/asf/commons-dbutils/diff/e672a7b5

Branch: refs/heads/master
Commit: e672a7b526b05c5cc36b09fd589e03d87c2c7076
Parents: 8d41daf
Author: Gary Gregory <ga...@gmail.com>
Authored: Fri May 4 10:14:20 2018 -0600
Committer: Gary Gregory <ga...@gmail.com>
Committed: Fri May 4 10:14:20 2018 -0600

----------------------------------------------------------------------
 .../commons/dbutils/AbstractQueryRunner.java    | 1210 +++++++++---------
 1 file changed, 605 insertions(+), 605 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-dbutils/blob/e672a7b5/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java b/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java
index 4e3a56c..f0c3590 100644
--- a/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java
+++ b/src/main/java/org/apache/commons/dbutils/AbstractQueryRunner.java
@@ -1,605 +1,605 @@
-/*
- * 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</code>.
-     *
-     * @param pmdKnownBroken
-     *            Some drivers don't support
-     *            {@link ParameterMetaData#getParameterType(int) }; if
-     *            <code>pmdKnownBroken</code> is set to true, we won't even try
-     *            it; if false, we'll try it, and if it breaks, we'll remember
-     *            not to use it again.
-     */
-    public AbstractQueryRunner(boolean pmdKnownBroken) {
-        this.pmdKnownBroken = pmdKnownBroken;
-        ds = null;
-        this.stmtConfig = null;
-    }
-
-    /**
-     * Constructor to provide a <code>DataSource</code>. Methods that do not
-     * take a <code>Connection</code> parameter will retrieve connections from
-     * this <code>DataSource</code>.
-     *
-     * @param ds
-     *            The <code>DataSource</code> to retrieve connections from.
-     */
-    public AbstractQueryRunner(DataSource ds) {
-        this.ds = ds;
-        this.stmtConfig = null;
-    }
-
-    /**
-     * Constructor for QueryRunner that takes a <code>StatementConfiguration</code> to configure statements when
-     * preparing them.
-     *
-     * @param stmtConfig The configuration to apply to statements when they are prepared.
-     */
-    public AbstractQueryRunner(StatementConfiguration stmtConfig) {
-        this.ds = null;
-        this.stmtConfig = stmtConfig;
-    }
-
-    /**
-     * Constructor to provide a <code>DataSource</code> and control the use of
-     * <code>ParameterMetaData</code>. Methods that do not take a
-     * <code>Connection</code> parameter will retrieve connections from this
-     * <code>DataSource</code>.
-     *
-     * @param ds
-     *            The <code>DataSource</code> to retrieve connections from.
-     * @param pmdKnownBroken
-     *            Some drivers don't support
-     *            {@link ParameterMetaData#getParameterType(int) }; if
-     *            <code>pmdKnownBroken</code> is set to true, we won't even try
-     *            it; if false, we'll try it, and if it breaks, we'll remember
-     *            not to use it again.
-     */
-    public AbstractQueryRunner(DataSource ds, boolean pmdKnownBroken) {
-        this.pmdKnownBroken = pmdKnownBroken;
-        this.ds = ds;
-        this.stmtConfig = null;
-    }
-
-    /**
-     * Constructor for QueryRunner that takes a <code>DataSource</code> to use and a <code>StatementConfiguration</code>.
-     *
-     * Methods that do not take a <code>Connection</code> parameter will retrieve connections from this
-     * <code>DataSource</code>.
-     *
-     * @param ds The <code>DataSource</code> to retrieve connections from.
-     * @param stmtConfig The configuration to apply to statements when they are prepared.
-     */
-    public AbstractQueryRunner(DataSource ds, StatementConfiguration stmtConfig) {
-        this.ds = ds;
-        this.stmtConfig = stmtConfig;
-    }
-
-    /**
-     * Constructor for QueryRunner that takes a <code>DataSource</code>, a <code>StatementConfiguration</code>, and
-     * controls the use of <code>ParameterMetaData</code>.  Methods that do not take a <code>Connection</code> parameter
-     * will retrieve connections from this <code>DataSource</code>.
-     *
-     * @param ds The <code>DataSource</code> to retrieve connections from.
-     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
-     * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
-     * and if it breaks, we'll remember not to use it again.
-     * @param stmtConfig The configuration to apply to statements when they are prepared.
-     */
-    public AbstractQueryRunner(DataSource ds, boolean pmdKnownBroken, StatementConfiguration stmtConfig) {
-        this.pmdKnownBroken = pmdKnownBroken;
-        this.ds = ds;
-        this.stmtConfig = stmtConfig;
-    }
-
-    /**
-     * Returns the <code>DataSource</code> this runner is using.
-     * <code>QueryRunner</code> methods always call this method to get the
-     * <code>DataSource</code> so subclasses can provide specialized behavior.
-     *
-     * @return DataSource the runner is using
-     */
-    public DataSource getDataSource() {
-        return this.ds;
-    }
-
-    /**
-     * Some drivers don't support
-     * {@link ParameterMetaData#getParameterType(int) }; if
-     * <code>pmdKnownBroken</code> is set to true, we won't even try it; if
-     * false, we'll try it, and if it breaks, we'll remember not to use it
-     * again.
-     *
-     * @return the flag to skip (or not)
-     *         {@link ParameterMetaData#getParameterType(int) }
-     * @since 1.4
-     */
-    public boolean isPmdKnownBroken() {
-        return pmdKnownBroken;
-    }
-
-    /**
-     * Factory method that creates and initializes a
-     * <code>PreparedStatement</code> object for the given SQL.
-     * <code>QueryRunner</code> methods always call this method to prepare
-     * statements for them. Subclasses can override this method to provide
-     * special PreparedStatement configuration if needed. This implementation
-     * simply calls <code>conn.prepareStatement(sql)</code>.
-     *
-     * @param conn
-     *            The <code>Connection</code> used to create the
-     *            <code>PreparedStatement</code>
-     * @param sql
-     *            The SQL statement to prepare.
-     * @return An initialized <code>PreparedStatement</code>.
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    protected PreparedStatement prepareStatement(Connection conn, String sql)
-            throws SQLException {
-
-        PreparedStatement ps = conn.prepareStatement(sql);
-        try {
-            configureStatement(ps);
-        } catch (SQLException e) {
-            ps.close();
-            throw e;
-        }
-        return ps;
-    }
-
-    /**
-     * Factory method that creates and initializes a
-     * <code>PreparedStatement</code> object for the given SQL.
-     * <code>QueryRunner</code> methods always call this method to prepare
-     * statements for them. Subclasses can override this method to provide
-     * special PreparedStatement configuration if needed. This implementation
-     * simply calls <code>conn.prepareStatement(sql, returnedKeys)</code>
-     * which will result in the ability to retrieve the automatically-generated
-     * keys from an auto_increment column.
-     *
-     * @param conn
-     *            The <code>Connection</code> used to create the
-     *            <code>PreparedStatement</code>
-     * @param sql
-     *            The SQL statement to prepare.
-     * @param returnedKeys
-     *            Flag indicating whether to return generated keys or not.
-     *
-     * @return An initialized <code>PreparedStatement</code>.
-     * @throws SQLException
-     *             if a database access error occurs
-     * @since 1.6
-     */
-    protected PreparedStatement prepareStatement(Connection conn, String sql, int returnedKeys)
-            throws SQLException {
-
-        PreparedStatement ps = conn.prepareStatement(sql, returnedKeys);
-        try {
-            configureStatement(ps);
-        } catch (SQLException e) {
-            ps.close();
-            throw e;
-        }
-        return ps;
-    }
-
-    private void configureStatement(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());
-            }
-        }
-    }
-
-    /**
-     * Factory method that creates and initializes a
-     * <code>CallableStatement</code> object for the given SQL.
-     * <code>QueryRunner</code> 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)</code>.
-     *
-     * @param conn
-     *            The <code>Connection</code> used to create the
-     *            <code>CallableStatement</code>
-     * @param sql
-     *            The SQL statement to prepare.
-     * @return An initialized <code>CallableStatement</code>.
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    protected CallableStatement prepareCall(Connection conn, String sql)
-            throws SQLException {
-
-        return conn.prepareCall(sql);
-    }
-
-    /**
-     * Factory method that creates and initializes a <code>Connection</code>
-     * object. <code>QueryRunner</code> methods always call this method to
-     * retrieve connections from its DataSource. Subclasses can override this
-     * method to provide special <code>Connection</code> configuration if
-     * needed. This implementation simply calls <code>ds.getConnection()</code>.
-     *
-     * @return An initialized <code>Connection</code>.
-     * @throws SQLException
-     *             if a database access error occurs
-     * @since DbUtils 1.1
-     */
-    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();
-    }
-
-    /**
-     * Fill the <code>PreparedStatement</code> replacement parameters with the
-     * given objects.
-     *
-     * @param stmt
-     *            PreparedStatement to fill
-     * @param params
-     *            Query replacement parameters; <code>null</code> is a valid
-     *            value to pass in.
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    public void fillStatement(PreparedStatement stmt, Object... params)
-            throws SQLException {
-
-        // check the parameter count, if we can
-        ParameterMetaData pmd = null;
-        if (!pmdKnownBroken) {
-            try {
-                pmd = stmt.getParameterMetaData();
-                if (pmd == null) { // can be returned by implementations that don't support the method
-                    pmdKnownBroken = true;
-                } else {
-                    int stmtCount = pmd.getParameterCount();
-                    int paramsCount = params == null ? 0 : params.length;
-
-                    if (stmtCount != paramsCount) {
-                        throw new SQLException("Wrong number of parameters: expected "
-                                + stmtCount + ", was given " + paramsCount);
-                    }
-                }
-            } catch (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 (SQLException e) {
-                        pmdKnownBroken = true;
-                    }
-                }
-                stmt.setNull(i + 1, sqlType);
-            }
-        }
-    }
-
-    /**
-     * Fill the <code>PreparedStatement</code> replacement parameters with the
-     * given object's bean property values.
-     *
-     * @param stmt
-     *            PreparedStatement to fill
-     * @param bean
-     *            a JavaBean object
-     * @param properties
-     *            an ordered array of properties; this gives the order to insert
-     *            values in the statement
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    public void fillStatementWithBean(PreparedStatement stmt, Object bean,
-            PropertyDescriptor[] properties) throws SQLException {
-        Object[] params = new Object[properties.length];
-        for (int i = 0; i < properties.length; i++) {
-            PropertyDescriptor property = properties[i];
-            Object value = null;
-            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, new Object[0]);
-            } catch (InvocationTargetException e) {
-                throw new RuntimeException("Couldn't invoke method: " + method,
-                        e);
-            } catch (IllegalArgumentException e) {
-                throw new RuntimeException(
-                        "Couldn't invoke method with 0 arguments: " + method, e);
-            } catch (IllegalAccessException e) {
-                throw new RuntimeException("Couldn't invoke method: " + method,
-                        e);
-            }
-            params[i] = value;
-        }
-        fillStatement(stmt, params);
-    }
-
-    /**
-     * Fill the <code>PreparedStatement</code> replacement parameters with the
-     * given object's bean property values.
-     *
-     * @param stmt
-     *            PreparedStatement to fill
-     * @param bean
-     *            A JavaBean object
-     * @param 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(PreparedStatement stmt, Object bean,
-            String... propertyNames) throws SQLException {
-        PropertyDescriptor[] descriptors;
-        try {
-            descriptors = Introspector.getBeanInfo(bean.getClass())
-                    .getPropertyDescriptors();
-        } catch (IntrospectionException e) {
-            throw new RuntimeException("Couldn't introspect bean "
-                    + bean.getClass().toString(), e);
-        }
-        PropertyDescriptor[] sorted = new PropertyDescriptor[propertyNames.length];
-        for (int i = 0; i < propertyNames.length; i++) {
-            String propertyName = propertyNames[i];
-            if (propertyName == null) {
-                throw new NullPointerException("propertyName can't be null: "
-                        + i);
-            }
-            boolean found = false;
-            for (int j = 0; j < descriptors.length; j++) {
-                PropertyDescriptor descriptor = descriptors[j];
-                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);
-    }
-
-    /**
-     * Throws a new exception with a more informative error message.
-     *
-     * @param cause
-     *            The original exception that will be chained to the new
-     *            exception when it's rethrown.
-     *
-     * @param sql
-     *            The query that was executing when the exception happened.
-     *
-     * @param params
-     *            The query replacement parameters; <code>null</code> is a valid
-     *            value to pass in.
-     *
-     * @throws SQLException
-     *             if a database access error occurs
-     */
-    protected void rethrow(SQLException cause, String sql, Object... params)
-            throws SQLException {
-
-        String causeMessage = cause.getMessage();
-        if (causeMessage == null) {
-            causeMessage = "";
-        }
-        StringBuffer msg = new StringBuffer(causeMessage);
-
-        msg.append(" Query: ");
-        msg.append(sql);
-        msg.append(" Parameters: ");
-
-        if (params == null) {
-            msg.append("[]");
-        } else {
-            msg.append(Arrays.deepToString(params));
-        }
-
-        SQLException e = new SQLException(msg.toString(), cause.getSQLState(),
-                cause.getErrorCode());
-        e.setNextException(cause);
-
-        throw e;
-    }
-
-    /**
-     * Wrap the <code>ResultSet</code> in a decorator before processing it. This
-     * implementation returns the <code>ResultSet</code> it is given without any
-     * decoration.
-     *
-     * <p>
-     * Often, the implementation of this method can be done in an anonymous
-     * inner class like this:
-     * </p>
-     *
-     * <pre>
-     * QueryRunner run = new QueryRunner() {
-     *     protected ResultSet wrap(ResultSet rs) {
-     *         return StringTrimmedResultSet.wrap(rs);
-     *     }
-     * };
-     * </pre>
-     *
-     * @param rs
-     *            The <code>ResultSet</code> to decorate; never
-     *            <code>null</code>.
-     * @return The <code>ResultSet</code> wrapped in some decorator.
-     */
-    protected ResultSet wrap(ResultSet rs) {
-        return rs;
-    }
-
-    /**
-     * Close a <code>Connection</code>. This implementation avoids closing if
-     * null and does <strong>not</strong> suppress any exceptions. Subclasses
-     * can override to provide special handling like logging.
-     *
-     * @param conn
-     *            Connection to close
-     * @throws SQLException
-     *             if a database access error occurs
-     * @since DbUtils 1.1
-     */
-    protected void close(Connection conn) throws SQLException {
-        DbUtils.close(conn);
-    }
-
-    /**
-     * Close a <code>Statement</code>. This implementation avoids closing if
-     * null and does <strong>not</strong> suppress any exceptions. Subclasses
-     * can override to provide special handling like logging.
-     *
-     * @param stmt
-     *            Statement to close
-     * @throws SQLException
-     *             if a database access error occurs
-     * @since DbUtils 1.1
-     */
-    protected void close(Statement stmt) throws SQLException {
-        DbUtils.close(stmt);
-    }
-
-    /**
-     * Close a <code>ResultSet</code>. This implementation avoids closing if
-     * null and does <strong>not</strong> suppress any exceptions. Subclasses
-     * can override to provide special handling like logging.
-     *
-     * @param rs
-     *            ResultSet to close
-     * @throws SQLException
-     *             if a database access error occurs
-     * @since DbUtils 1.1
-     */
-    protected void close(ResultSet rs) throws SQLException {
-        DbUtils.close(rs);
-    }
-
-}
+/*
+ * 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</code>.
+     *
+     * @param pmdKnownBroken
+     *            Some drivers don't support
+     *            {@link ParameterMetaData#getParameterType(int) }; if
+     *            <code>pmdKnownBroken</code> is set to true, we won't even try
+     *            it; if false, we'll try it, and if it breaks, we'll remember
+     *            not to use it again.
+     */
+    public AbstractQueryRunner(boolean pmdKnownBroken) {
+        this.pmdKnownBroken = pmdKnownBroken;
+        ds = null;
+        this.stmtConfig = null;
+    }
+
+    /**
+     * Constructor to provide a <code>DataSource</code>. Methods that do not
+     * take a <code>Connection</code> parameter will retrieve connections from
+     * this <code>DataSource</code>.
+     *
+     * @param ds
+     *            The <code>DataSource</code> to retrieve connections from.
+     */
+    public AbstractQueryRunner(DataSource ds) {
+        this.ds = ds;
+        this.stmtConfig = null;
+    }
+
+    /**
+     * Constructor to provide a <code>DataSource</code> and control the use of
+     * <code>ParameterMetaData</code>. Methods that do not take a
+     * <code>Connection</code> parameter will retrieve connections from this
+     * <code>DataSource</code>.
+     *
+     * @param ds
+     *            The <code>DataSource</code> to retrieve connections from.
+     * @param pmdKnownBroken
+     *            Some drivers don't support
+     *            {@link ParameterMetaData#getParameterType(int) }; if
+     *            <code>pmdKnownBroken</code> is set to true, we won't even try
+     *            it; if false, we'll try it, and if it breaks, we'll remember
+     *            not to use it again.
+     */
+    public AbstractQueryRunner(DataSource ds, boolean pmdKnownBroken) {
+        this.pmdKnownBroken = pmdKnownBroken;
+        this.ds = ds;
+        this.stmtConfig = null;
+    }
+
+    /**
+     * Constructor for QueryRunner that takes a <code>DataSource</code>, a <code>StatementConfiguration</code>, and
+     * controls the use of <code>ParameterMetaData</code>.  Methods that do not take a <code>Connection</code> parameter
+     * will retrieve connections from this <code>DataSource</code>.
+     *
+     * @param ds The <code>DataSource</code> to retrieve connections from.
+     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
+     * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
+     * and if it breaks, we'll remember not to use it again.
+     * @param stmtConfig The configuration to apply to statements when they are prepared.
+     */
+    public AbstractQueryRunner(DataSource ds, boolean pmdKnownBroken, StatementConfiguration stmtConfig) {
+        this.pmdKnownBroken = pmdKnownBroken;
+        this.ds = ds;
+        this.stmtConfig = stmtConfig;
+    }
+
+    /**
+     * Constructor for QueryRunner that takes a <code>DataSource</code> to use and a <code>StatementConfiguration</code>.
+     *
+     * Methods that do not take a <code>Connection</code> parameter will retrieve connections from this
+     * <code>DataSource</code>.
+     *
+     * @param ds The <code>DataSource</code> to retrieve connections from.
+     * @param stmtConfig The configuration to apply to statements when they are prepared.
+     */
+    public AbstractQueryRunner(DataSource ds, StatementConfiguration stmtConfig) {
+        this.ds = ds;
+        this.stmtConfig = stmtConfig;
+    }
+
+    /**
+     * Constructor for QueryRunner that takes a <code>StatementConfiguration</code> to configure statements when
+     * preparing them.
+     *
+     * @param stmtConfig The configuration to apply to statements when they are prepared.
+     */
+    public AbstractQueryRunner(StatementConfiguration stmtConfig) {
+        this.ds = null;
+        this.stmtConfig = stmtConfig;
+    }
+
+    /**
+     * Close a <code>Connection</code>. This implementation avoids closing if
+     * null and does <strong>not</strong> suppress any exceptions. Subclasses
+     * can override to provide special handling like logging.
+     *
+     * @param conn
+     *            Connection to close
+     * @throws SQLException
+     *             if a database access error occurs
+     * @since DbUtils 1.1
+     */
+    protected void close(Connection conn) throws SQLException {
+        DbUtils.close(conn);
+    }
+
+    /**
+     * Close a <code>ResultSet</code>. This implementation avoids closing if
+     * null and does <strong>not</strong> suppress any exceptions. Subclasses
+     * can override to provide special handling like logging.
+     *
+     * @param rs
+     *            ResultSet to close
+     * @throws SQLException
+     *             if a database access error occurs
+     * @since DbUtils 1.1
+     */
+    protected void close(ResultSet rs) throws SQLException {
+        DbUtils.close(rs);
+    }
+
+    /**
+     * Close a <code>Statement</code>. This implementation avoids closing if
+     * null and does <strong>not</strong> suppress any exceptions. Subclasses
+     * can override to provide special handling like logging.
+     *
+     * @param stmt
+     *            Statement to close
+     * @throws SQLException
+     *             if a database access error occurs
+     * @since DbUtils 1.1
+     */
+    protected void close(Statement stmt) throws SQLException {
+        DbUtils.close(stmt);
+    }
+
+    private void configureStatement(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</code> replacement parameters with the
+     * given objects.
+     *
+     * @param stmt
+     *            PreparedStatement to fill
+     * @param params
+     *            Query replacement parameters; <code>null</code> is a valid
+     *            value to pass in.
+     * @throws SQLException
+     *             if a database access error occurs
+     */
+    public void fillStatement(PreparedStatement stmt, Object... params)
+            throws SQLException {
+
+        // check the parameter count, if we can
+        ParameterMetaData pmd = null;
+        if (!pmdKnownBroken) {
+            try {
+                pmd = stmt.getParameterMetaData();
+                if (pmd == null) { // can be returned by implementations that don't support the method
+                    pmdKnownBroken = true;
+                } else {
+                    int stmtCount = pmd.getParameterCount();
+                    int paramsCount = params == null ? 0 : params.length;
+
+                    if (stmtCount != paramsCount) {
+                        throw new SQLException("Wrong number of parameters: expected "
+                                + stmtCount + ", was given " + paramsCount);
+                    }
+                }
+            } catch (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 (SQLException e) {
+                        pmdKnownBroken = true;
+                    }
+                }
+                stmt.setNull(i + 1, sqlType);
+            }
+        }
+    }
+
+    /**
+     * Fill the <code>PreparedStatement</code> replacement parameters with the
+     * given object's bean property values.
+     *
+     * @param stmt
+     *            PreparedStatement to fill
+     * @param bean
+     *            a JavaBean object
+     * @param properties
+     *            an ordered array of properties; this gives the order to insert
+     *            values in the statement
+     * @throws SQLException
+     *             if a database access error occurs
+     */
+    public void fillStatementWithBean(PreparedStatement stmt, Object bean,
+            PropertyDescriptor[] properties) throws SQLException {
+        Object[] params = new Object[properties.length];
+        for (int i = 0; i < properties.length; i++) {
+            PropertyDescriptor property = properties[i];
+            Object value = null;
+            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, new Object[0]);
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException("Couldn't invoke method: " + method,
+                        e);
+            } catch (IllegalArgumentException e) {
+                throw new RuntimeException(
+                        "Couldn't invoke method with 0 arguments: " + method, e);
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException("Couldn't invoke method: " + method,
+                        e);
+            }
+            params[i] = value;
+        }
+        fillStatement(stmt, params);
+    }
+
+    /**
+     * Fill the <code>PreparedStatement</code> replacement parameters with the
+     * given object's bean property values.
+     *
+     * @param stmt
+     *            PreparedStatement to fill
+     * @param bean
+     *            A JavaBean object
+     * @param 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(PreparedStatement stmt, Object bean,
+            String... propertyNames) throws SQLException {
+        PropertyDescriptor[] descriptors;
+        try {
+            descriptors = Introspector.getBeanInfo(bean.getClass())
+                    .getPropertyDescriptors();
+        } catch (IntrospectionException e) {
+            throw new RuntimeException("Couldn't introspect bean "
+                    + bean.getClass().toString(), e);
+        }
+        PropertyDescriptor[] sorted = new PropertyDescriptor[propertyNames.length];
+        for (int i = 0; i < propertyNames.length; i++) {
+            String propertyName = propertyNames[i];
+            if (propertyName == null) {
+                throw new NullPointerException("propertyName can't be null: "
+                        + i);
+            }
+            boolean found = false;
+            for (int j = 0; j < descriptors.length; j++) {
+                PropertyDescriptor descriptor = descriptors[j];
+                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</code> this runner is using.
+     * <code>QueryRunner</code> methods always call this method to get the
+     * <code>DataSource</code> so subclasses can provide specialized behavior.
+     *
+     * @return DataSource the runner is using
+     */
+    public DataSource getDataSource() {
+        return this.ds;
+    }
+
+    /**
+     * Some drivers don't support
+     * {@link ParameterMetaData#getParameterType(int) }; if
+     * <code>pmdKnownBroken</code> is set to true, we won't even try it; if
+     * false, we'll try it, and if it breaks, we'll remember not to use it
+     * again.
+     *
+     * @return the flag to skip (or not)
+     *         {@link ParameterMetaData#getParameterType(int) }
+     * @since 1.4
+     */
+    public boolean isPmdKnownBroken() {
+        return pmdKnownBroken;
+    }
+
+    /**
+     * Factory method that creates and initializes a
+     * <code>CallableStatement</code> object for the given SQL.
+     * <code>QueryRunner</code> 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)</code>.
+     *
+     * @param conn
+     *            The <code>Connection</code> used to create the
+     *            <code>CallableStatement</code>
+     * @param sql
+     *            The SQL statement to prepare.
+     * @return An initialized <code>CallableStatement</code>.
+     * @throws SQLException
+     *             if a database access error occurs
+     */
+    protected CallableStatement prepareCall(Connection conn, String sql)
+            throws SQLException {
+
+        return conn.prepareCall(sql);
+    }
+
+    /**
+     * Factory method that creates and initializes a <code>Connection</code>
+     * object. <code>QueryRunner</code> methods always call this method to
+     * retrieve connections from its DataSource. Subclasses can override this
+     * method to provide special <code>Connection</code> configuration if
+     * needed. This implementation simply calls <code>ds.getConnection()</code>.
+     *
+     * @return An initialized <code>Connection</code>.
+     * @throws SQLException
+     *             if a database access error occurs
+     * @since DbUtils 1.1
+     */
+    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</code> object for the given SQL.
+     * <code>QueryRunner</code> methods always call this method to prepare
+     * statements for them. Subclasses can override this method to provide
+     * special PreparedStatement configuration if needed. This implementation
+     * simply calls <code>conn.prepareStatement(sql)</code>.
+     *
+     * @param conn
+     *            The <code>Connection</code> used to create the
+     *            <code>PreparedStatement</code>
+     * @param sql
+     *            The SQL statement to prepare.
+     * @return An initialized <code>PreparedStatement</code>.
+     * @throws SQLException
+     *             if a database access error occurs
+     */
+    protected PreparedStatement prepareStatement(Connection conn, String sql)
+            throws SQLException {
+
+        PreparedStatement ps = conn.prepareStatement(sql);
+        try {
+            configureStatement(ps);
+        } catch (SQLException e) {
+            ps.close();
+            throw e;
+        }
+        return ps;
+    }
+
+    /**
+     * Factory method that creates and initializes a
+     * <code>PreparedStatement</code> object for the given SQL.
+     * <code>QueryRunner</code> methods always call this method to prepare
+     * statements for them. Subclasses can override this method to provide
+     * special PreparedStatement configuration if needed. This implementation
+     * simply calls <code>conn.prepareStatement(sql, returnedKeys)</code>
+     * which will result in the ability to retrieve the automatically-generated
+     * keys from an auto_increment column.
+     *
+     * @param conn
+     *            The <code>Connection</code> used to create the
+     *            <code>PreparedStatement</code>
+     * @param sql
+     *            The SQL statement to prepare.
+     * @param returnedKeys
+     *            Flag indicating whether to return generated keys or not.
+     *
+     * @return An initialized <code>PreparedStatement</code>.
+     * @throws SQLException
+     *             if a database access error occurs
+     * @since 1.6
+     */
+    protected PreparedStatement prepareStatement(Connection conn, String sql, int returnedKeys)
+            throws SQLException {
+
+        PreparedStatement ps = conn.prepareStatement(sql, returnedKeys);
+        try {
+            configureStatement(ps);
+        } catch (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</code> is a valid
+     *            value to pass in.
+     *
+     * @throws SQLException
+     *             if a database access error occurs
+     */
+    protected void rethrow(SQLException cause, String sql, Object... params)
+            throws SQLException {
+
+        String causeMessage = cause.getMessage();
+        if (causeMessage == null) {
+            causeMessage = "";
+        }
+        StringBuffer msg = new StringBuffer(causeMessage);
+
+        msg.append(" Query: ");
+        msg.append(sql);
+        msg.append(" Parameters: ");
+
+        if (params == null) {
+            msg.append("[]");
+        } else {
+            msg.append(Arrays.deepToString(params));
+        }
+
+        SQLException e = new SQLException(msg.toString(), cause.getSQLState(),
+                cause.getErrorCode());
+        e.setNextException(cause);
+
+        throw e;
+    }
+
+    /**
+     * Wrap the <code>ResultSet</code> in a decorator before processing it. This
+     * implementation returns the <code>ResultSet</code> it is given without any
+     * decoration.
+     *
+     * <p>
+     * Often, the implementation of this method can be done in an anonymous
+     * inner class like this:
+     * </p>
+     *
+     * <pre>
+     * QueryRunner run = new QueryRunner() {
+     *     protected ResultSet wrap(ResultSet rs) {
+     *         return StringTrimmedResultSet.wrap(rs);
+     *     }
+     * };
+     * </pre>
+     *
+     * @param rs
+     *            The <code>ResultSet</code> to decorate; never
+     *            <code>null</code>.
+     * @return The <code>ResultSet</code> wrapped in some decorator.
+     */
+    protected ResultSet wrap(ResultSet rs) {
+        return rs;
+    }
+
+}