You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ws...@apache.org on 2019/12/31 00:32:30 UTC

[commons-dbutils] branch 2_0 updated: Merge with github.com/wspeirs/sop4j-dbutils

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

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


The following commit(s) were added to refs/heads/2_0 by this push:
     new 180c099  Merge with github.com/wspeirs/sop4j-dbutils
180c099 is described below

commit 180c0990b1f0da4d0039904b68cc6b57f00558e5
Author: William Speirs <bi...@gmail.com>
AuthorDate: Mon Dec 30 19:27:42 2019 -0500

    Merge with github.com/wspeirs/sop4j-dbutils
---
 .gitignore                                         |   1 +
 pom.xml                                            |  19 +-
 .../apache/commons/dbutils2/AbstractExecutor.java  | 120 ++++-----
 .../commons/dbutils2/BaseResultSetHandler.java     | 110 +++++----
 .../apache/commons/dbutils2/BasicRowProcessor.java |   2 +-
 .../org/apache/commons/dbutils2/BatchExecutor.java |  69 ++----
 .../commons/dbutils2/BatchInsertExecutor.java      |   3 +-
 .../org/apache/commons/dbutils2/BeanProcessor.java |  19 +-
 .../org/apache/commons/dbutils2/EntityUtils.java   | 251 +++++++++++++++++++
 .../apache/commons/dbutils2/InsertExecutor.java    |  10 +-
 .../org/apache/commons/dbutils2/QueryRunner.java   | 273 +++++++++++++++++++++
 .../commons/dbutils2/AbstractExecutorTest.java     |  14 +-
 .../apache/commons/dbutils2/AsyncExecutorTest.java |   7 +-
 .../dbutils2/BaseResultSetHandlerTestCase.java     |   1 -
 .../org/apache/commons/dbutils2/BaseTestCase.java  |   9 +-
 .../commons/dbutils2/BasicRowProcessorTest.java    |   3 -
 .../apache/commons/dbutils2/BatchExecutorTest.java |  61 ++++-
 .../commons/dbutils2/BatchInsertExecutorTest.java  |   8 +-
 .../apache/commons/dbutils2/BeanProcessorTest.java |   3 -
 .../org/apache/commons/dbutils2/DbUtilsTest.java   |   1 -
 .../java/org/apache/commons/dbutils2/EnumTest.java |  36 +++
 .../dbutils2/GenerousBeanProcessorTest.java        |   2 +-
 .../commons/dbutils2/InsertExecutorTest.java       |  12 +-
 .../org/apache/commons/dbutils2/MockResultSet.java |   4 +-
 .../commons/dbutils2/MockResultSetMetaData.java    |   2 -
 .../apache/commons/dbutils2/ProxyFactoryTest.java  |   2 -
 .../apache/commons/dbutils2/QueryExecutorTest.java |   4 +-
 .../apache/commons/dbutils2/QueryLoaderTest.java   |   5 +-
 .../apache/commons/dbutils2/QueryRunnerTest.java   |   3 +-
 .../commons/dbutils2/ResultSetIteratorTest.java    |   2 -
 .../java/org/apache/commons/dbutils2/TestBean.java |  14 ++
 .../commons/dbutils2/UpdateExecutorTest.java       |   8 +-
 .../dbutils2/handlers/BeanMapHandlerTest.java      |   4 +-
 .../dbutils2/handlers/ColumnListHandlerTest.java   |   1 -
 .../dbutils2/handlers/KeyedHandlerTest.java        |   1 -
 .../commons/dbutils2/handlers/MapHandlerTest.java  |   1 -
 .../dbutils2/handlers/MapListHandlerTest.java      |   1 -
 .../dbutils2/handlers/ScalarHandlerTest.java       |   1 -
 .../wrappers/SqlNullCheckedResultSetTest.java      |   1 -
 .../wrappers/StringTrimmedResultSetTest.java       |   2 -
 40 files changed, 849 insertions(+), 241 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e420ee4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+target/*
diff --git a/pom.xml b/pom.xml
index bd7096d..07dacbc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -189,10 +189,27 @@
       <name>Benedikt Ritter</name>
       <email>bene at systemoutprintln dot de</email>
     </contributor>
-  </contributors>
+    <contributor>
+      <name>William Speirs</name>
+      <email>bill.speirs@gmail.com</email>
+      <roles>
+        <role>Java Developer</role>
+      </roles>
+    </contributor>
+   </contributors>
 
   <dependencies>
     <dependency>
+      <groupId>javax.persistence</groupId>
+      <artifactId>javax.persistence-api</artifactId>
+      <version>2.2</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-beanutils</groupId>
+      <artifactId>commons-beanutils</artifactId>
+      <version>1.9.4</version>
+    </dependency>
+     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.11</version>
diff --git a/src/main/java/org/apache/commons/dbutils2/AbstractExecutor.java b/src/main/java/org/apache/commons/dbutils2/AbstractExecutor.java
index 89f99e7..f2356cb 100644
--- a/src/main/java/org/apache/commons/dbutils2/AbstractExecutor.java
+++ b/src/main/java/org/apache/commons/dbutils2/AbstractExecutor.java
@@ -38,6 +38,7 @@ import java.util.regex.Pattern;
 abstract class AbstractExecutor<T extends AbstractExecutor<T>> {
 
     private static final String COLON = ":";  // TODO: change this to any character
+    private static final Pattern PARAM_PATTERN = Pattern.compile("(:\\w+)");
 
     private final Connection conn;
     private final String sql;
@@ -48,13 +49,16 @@ abstract class AbstractExecutor<T extends AbstractExecutor<T>> {
     private int currentPosition = 0;
 
     public AbstractExecutor(final Connection conn, final String sql) throws SQLException {
+        this(conn, sql, Statement.NO_GENERATED_KEYS);
+    }
+
+    public AbstractExecutor(final Connection conn, final String sql, final int autoGeneratedKeys) throws SQLException {
         this.conn = conn;
         this.sql = sql;
         this.paramPosMap = new HashMap<String, List<Integer>>();
         this.paramValueMap = new HashMap<String, Object>();
 
-        final Pattern paramPattern = Pattern.compile("(:\\w+)");
-        final Matcher matcher = paramPattern.matcher(sql);
+        final Matcher matcher = PARAM_PATTERN.matcher(sql);
 
         // go through finding params
         while (matcher.find()) {
@@ -62,7 +66,7 @@ abstract class AbstractExecutor<T extends AbstractExecutor<T>> {
         }
 
         // replace all of the :names with ?, and create a prepared statement
-        stmt = conn.prepareStatement(sql.replaceAll(":\\w+", "\\?"));
+        stmt = conn.prepareStatement(sql.replaceAll(":\\w+", "\\?"), autoGeneratedKeys);
     }
 
     /**
@@ -115,21 +119,27 @@ abstract class AbstractExecutor<T extends AbstractExecutor<T>> {
      * @throws SQLException if there are unmapped params.
      */
     void throwIfUnmappedParams() throws SQLException {
-        if (paramPosMap.size() != 0) {
-            final Set<String> unmappedParams = paramPosMap.keySet();
-            final StringBuilder sb = new StringBuilder("There are unbound parameters: ");
+        // if the sizes are the same, then we've filled all the parameters
+        if(paramValueMap.size() == paramPosMap.size()) {
+            return;
+        }
 
-            for (String param:unmappedParams) {
-                sb.append(param);
-                sb.append(", ");
-            }
+        final StringBuilder sb = new StringBuilder("There are unbound parameters: ");
+        final Set<String> unboundParams = paramPosMap.keySet();
 
-            // remove the last comma
-            sb.delete(sb.length() - 2, sb.length());
+        // compute the set difference
+        unboundParams.removeAll(paramValueMap.keySet());
 
-            // throw our exception
-            throw new SQLException(sb.toString());
+        for (String param:unboundParams) {
+            sb.append(param);
+            sb.append(", ");
         }
+
+        // remove the last comma
+        sb.delete(sb.length() - 2, sb.length());
+
+        // throw our exception
+        throw new SQLException(sb.toString());
     }
 
     /**
@@ -140,8 +150,33 @@ abstract class AbstractExecutor<T extends AbstractExecutor<T>> {
      * @return this execution object to provide the fluent style.
      * @throws SQLException thrown if the parameter is not found, already bound, or there is an issue binding it.
      */
-    public T bind(final String name, final Object value) throws SQLException {
-        return bind(name, value, true);
+    public T bind(String name, final Object value) throws SQLException {
+        name = name.replace(COLON, ""); // so we can take ":name" or "name"
+
+        final List<Integer> pos = paramPosMap.get(name);
+
+        if (pos == null) {
+            throw new SQLException(name + " is not found in the SQL statement: " + getSql());
+        }
+
+        // make sure it isn't already bound
+        if(paramValueMap.containsKey(name)) {
+            throw new SQLException("You are attempting to bind the parameter " + name + " twice. It already has the value " + paramValueMap.get(name));
+        }
+
+        // go through and bind all of the positions for this name
+        for (Integer p:pos) {
+            stmt.setObject(p.intValue(), value);
+        }
+
+        // add the param and value to our map
+        paramValueMap.put(name, value);
+
+        // suppressed because the casting will always work here
+        @SuppressWarnings("unchecked")
+        final T ret = (T) this;
+
+        return ret;
     }
 
     /**
@@ -154,7 +189,7 @@ abstract class AbstractExecutor<T extends AbstractExecutor<T>> {
      * @throws SQLException throw if the parameter is not found, already bound, or there is an issue binding null.
      */
     public T bindNull(final String name) throws SQLException {
-        return bindNull(name, Types.VARCHAR, true);
+        return bindNull(name, Types.VARCHAR);
     }
 
     /**
@@ -165,23 +200,10 @@ abstract class AbstractExecutor<T extends AbstractExecutor<T>> {
      * @return this execution object to provide the fluent style.
      * @throws SQLException throw if the parameter is not found, already bound, or there is an issue binding null.
      */
-    public T bindNull(final String name, final int sqlType) throws SQLException {
-        return bindNull(name, sqlType, true);
-    }
-
-    /**
-     * Given a param name and sqlType, binds a null to that parameter.
-     * 
-     * @param name the name of the parameter.
-     * @param sqlType the type of the parameter.
-     * @param removeFromPosMap if the param should be removed from the pos map.
-     * @return this
-     * @throws SQLException if there is an SQLException during binding.
-     */
-    T bindNull(String name, int sqlType, boolean removeFromPosMap) throws SQLException {
+    public T bindNull(String name, final int sqlType) throws SQLException {
         name = name.replace(COLON, ""); // so we can take ":name" or "name"
 
-        final List<Integer> pos = removeFromPosMap ? paramPosMap.remove(name) : paramPosMap.get(name);
+        final List<Integer> pos = paramPosMap.get(name);
 
         if (pos == null) {
             throw new SQLException(name + " is not found in the SQL statement");
@@ -203,40 +225,6 @@ abstract class AbstractExecutor<T extends AbstractExecutor<T>> {
     }
 
     /**
-     * Binds value to name, but does not do the bookkeeping.
-     * 
-     * @param name the parameter name.
-     * @param value the value.
-     * @param removeFromPosMap if the param should be removed from the pos map.
-     * @return this
-     * @throws SQLException if there is an SQLException during binding.
-     */
-    T bind(String name, final Object value, boolean removeFromPosMap) throws SQLException {
-        name = name.replace(COLON, ""); // so we can take ":name" or "name"
-
-        final List<Integer> pos = removeFromPosMap ? paramPosMap.remove(name) : paramPosMap.get(name);
-
-        if (pos == null) {
-            throw new SQLException(name + " is not found in the SQL statement");
-        }
-
-        // go through and bind all of the positions for this name
-        for (Integer p:pos) {
-            // TODO: need to figure out how to bind NULL
-            stmt.setObject(p.intValue(), value);
-        }
-
-        // add the param and value to our map
-        paramValueMap.put(name, value);
-
-        // suppressed because the casting will always work here
-        @SuppressWarnings("unchecked")
-        final T ret = (T) this;
-
-        return ret;
-    }
-
-    /**
      * Used for batch calls so we can clear the map after the addBatch call.
      */
     void clearValueMap() {
diff --git a/src/main/java/org/apache/commons/dbutils2/BaseResultSetHandler.java b/src/main/java/org/apache/commons/dbutils2/BaseResultSetHandler.java
index 8cf2673..854e69e 100644
--- a/src/main/java/org/apache/commons/dbutils2/BaseResultSetHandler.java
+++ b/src/main/java/org/apache/commons/dbutils2/BaseResultSetHandler.java
@@ -78,7 +78,6 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
      *
      * @return An Object initialized with <code>ResultSet</code> data
      * @throws SQLException if a database access error occurs
-     * @see {@link ResultSetHandler#handle(ResultSet)}
      */
     protected abstract T handle() throws SQLException;
 
@@ -321,7 +320,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnIndex the index of the column.
-     * @return
+     * @return the Reader.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getCharacterStream(int)
      */
@@ -379,7 +378,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     /**
      * @param columnIndex the index of the column.
      * @param cal the Calendar.
-     * @return
+     * @return the Date.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getDate(int, java.util.Calendar)
      */
@@ -596,8 +595,8 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnIndex the index of the column.
-     * @param map
-     * @return
+     * @param map the map.
+     * @return the Object.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getObject(int, java.util.Map)
      */
@@ -607,7 +606,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnIndex the index of the column.
-     * @return
+     * @return the Object.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getObject(int)
      */
@@ -617,8 +616,8 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnLabel the column's label.
-     * @param map
-     * @return
+     * @param map the map.
+     * @return the Object.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getObject(java.lang.String, java.util.Map)
      */
@@ -628,7 +627,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnLabel the column's label.
-     * @return
+     * @return the Object.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getObject(java.lang.String)
      */
@@ -638,7 +637,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnIndex the index of the column.
-     * @return
+     * @return the Ref.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getRef(int)
      */
@@ -648,7 +647,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnLabel the column's label.
-     * @return
+     * @return the Ref.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getRef(java.lang.String)
      */
@@ -657,7 +656,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return the row.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getRow()
      */
@@ -667,7 +666,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnIndex the index of the column.
-     * @return
+     * @return the RowId.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getRowId(int)
      */
@@ -677,7 +676,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnLabel the column's label.
-     * @return
+     * @return the RowId.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getRowId(java.lang.String)
      */
@@ -687,7 +686,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnIndex the index of the column.
-     * @return
+     * @return the SQLXML.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getSQLXML(int)
      */
@@ -697,7 +696,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnLabel the column's label.
-     * @return
+     * @return the SQLXML.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getSQLXML(java.lang.String)
      */
@@ -707,7 +706,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnIndex the index of the column.
-     * @return
+     * @return the short.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getShort(int)
      */
@@ -717,7 +716,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnLabel the column's label.
-     * @return
+     * @return the short.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getShort(java.lang.String)
      */
@@ -726,7 +725,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return the Statement.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getStatement()
      */
@@ -736,7 +735,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnIndex the index of the column.
-     * @return
+     * @return the String.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getString(int)
      */
@@ -746,7 +745,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnLabel the column's label.
-     * @return
+     * @return the String.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getString(java.lang.String)
      */
@@ -757,7 +756,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     /**
      * @param columnIndex the index of the column.
      * @param cal the Calendar.
-     * @return
+     * @return the Time.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getTime(int, java.util.Calendar)
      */
@@ -767,7 +766,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnIndex the index of the column.
-     * @return
+     * @return the Time.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getTime(int)
      */
@@ -778,7 +777,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     /**
      * @param columnLabel the column's label.
      * @param cal the Calendar.
-     * @return
+     * @return the Time.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getTime(java.lang.String, java.util.Calendar)
      */
@@ -788,7 +787,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnLabel the column's label.
-     * @return
+     * @return the Time.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getTime(java.lang.String)
      */
@@ -799,7 +798,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     /**
      * @param columnIndex the index of the column.
      * @param cal the Calendar.
-     * @return
+     * @return the Timestamp.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getTimestamp(int, java.util.Calendar)
      */
@@ -809,7 +808,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnIndex the index of the column.
-     * @return
+     * @return the Timestamp.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getTimestamp(int)
      */
@@ -820,7 +819,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     /**
      * @param columnLabel the column's label.
      * @param cal the Calendar.
-     * @return
+     * @return the Timestamp.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getTimestamp(java.lang.String, java.util.Calendar)
      */
@@ -830,7 +829,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnLabel the column's label.
-     * @return
+     * @return the Timestamp.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getTimestamp(java.lang.String)
      */
@@ -839,7 +838,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return the type.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getType()
      */
@@ -849,7 +848,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnIndex the index of the column.
-     * @return
+     * @return the URL.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getURL(int)
      */
@@ -859,7 +858,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
 
     /**
      * @param columnLabel the column's label.
-     * @return
+     * @return the URL.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getURL(java.lang.String)
      */
@@ -868,7 +867,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return the SQLWarning.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#getWarnings()
      */
@@ -885,7 +884,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return true if it is after last.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#isAfterLast()
      */
@@ -894,7 +893,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return true if it is before first.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#isBeforeFirst()
      */
@@ -903,7 +902,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return true if closed.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#isClosed()
      */
@@ -912,7 +911,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return true if first.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#isFirst()
      */
@@ -921,7 +920,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return true if last.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#isLast()
      */
@@ -930,8 +929,8 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @param iface
-     * @return
+     * @param iface the interface.
+     * @return true if it is a wrapper for the interface.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.Wrapper#isWrapperFor(java.lang.Class)
      */
@@ -940,7 +939,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return true if last.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#last()
      */
@@ -965,7 +964,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return true if there is a next.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#next()
      */
@@ -974,7 +973,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return true if there is a pervious.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#previous()
      */
@@ -991,8 +990,8 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @param rows
-     * @return
+     * @param rows the rows.
+     * @return true if relative.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#relative(int)
      */
@@ -1001,7 +1000,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return true if row deleted.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#rowDeleted()
      */
@@ -1010,7 +1009,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return true if the row was inserted.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#rowInserted()
      */
@@ -1019,7 +1018,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return true if the row was updated.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#rowUpdated()
      */
@@ -1028,7 +1027,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @param direction
+     * @param direction the direction to fetch.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#setFetchDirection(int)
      */
@@ -1037,7 +1036,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @param rows
+     * @param rows the rows to fetch.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#setFetchSize(int)
      */
@@ -1046,8 +1045,8 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @param iface
-     * @return
+     * @param iface the interface.
+     * @return the unwrapped object.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.Wrapper#unwrap(java.lang.Class)
      */
@@ -1904,7 +1903,7 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
     }
 
     /**
-     * @return
+     * @return true if it was null.
      * @throws SQLException thrown if there is an SQL error.
      * @see java.sql.ResultSet#wasNull()
      */
@@ -1912,8 +1911,11 @@ public abstract class BaseResultSetHandler<T> implements ResultSetHandler<T> {
         return rs.wasNull();
     }
 
+    /**
+     * @return the adapted ResultSet
+     */
     protected final ResultSet getAdaptedResultSet() {
         return rs;
     }
 
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/apache/commons/dbutils2/BasicRowProcessor.java b/src/main/java/org/apache/commons/dbutils2/BasicRowProcessor.java
index 264bf57..b4164c3 100644
--- a/src/main/java/org/apache/commons/dbutils2/BasicRowProcessor.java
+++ b/src/main/java/org/apache/commons/dbutils2/BasicRowProcessor.java
@@ -146,7 +146,7 @@ public class BasicRowProcessor implements RowProcessor {
             if (null == columnName || 0 == columnName.length()) {
                 columnName = rsmd.getColumnName(i);
             }
-            result.put(columnName, rs.getObject(i));		
+            result.put(columnName, rs.getObject(i));
         }
 
         return result;
diff --git a/src/main/java/org/apache/commons/dbutils2/BatchExecutor.java b/src/main/java/org/apache/commons/dbutils2/BatchExecutor.java
index f175a9b..603c0fc 100644
--- a/src/main/java/org/apache/commons/dbutils2/BatchExecutor.java
+++ b/src/main/java/org/apache/commons/dbutils2/BatchExecutor.java
@@ -18,7 +18,6 @@ package org.apache.commons.dbutils2;
 
 import java.sql.Connection;
 import java.sql.SQLException;
-import java.sql.Types;
 
 /**
  * This class provides the ability to execute a batch of statements.
@@ -30,10 +29,25 @@ import java.sql.Types;
 public class BatchExecutor extends AbstractExecutor<BatchExecutor> {
 
     private final boolean closeConn;
+    private boolean addBatchCalled = false;
 
     /**
      * Constructs a BatchExecutor given a connection and SQL statement.
-     * 
+     *
+     * @param conn The connection to use during execution.
+     * @param sql The SQL statement.
+     * @param closeConnection If the connection should be closed or not.
+     * @param autoGeneratedKeys If the statement should return auto generated keys or not.
+     * @throws SQLException thrown if there is an error during execution.
+     */
+    BatchExecutor(final Connection conn, final String sql, final boolean closeConnection, final int autoGeneratedKeys) throws SQLException {
+        super(conn, sql, autoGeneratedKeys);
+        this.closeConn = closeConnection;
+    }
+
+    /**
+     * Constructs a BatchExecutor given a connection and SQL statement.
+     *
      * @param conn The connection to use during execution.
      * @param sql The SQL statement.
      * @param closeConnection If the connection should be closed or not.
@@ -53,47 +67,6 @@ public class BatchExecutor extends AbstractExecutor<BatchExecutor> {
     }
 
     /**
-     * Binds a parameter name to a value for a given statement.
-     * 
-     * @param name the name of the parameter.
-     * @param value the value to bind to the parameter.
-     * @return this object.
-     * @throws SQLException thrown if the statement number does not exist, or any other SQLException.
-     * @see org.apache.commons.dbutils2.UpdateExecutor#bind(String, Object)
-     */
-    @Override
-    public BatchExecutor bind(final String name, final Object value) throws SQLException {
-        return bind(name, value, false);
-    }
-
-    /**
-     * Binds null to a parameter.
-     * Types.VARCHAR is used as the type's parameter.
-     * This usually works, but fails with some Oracle and MS SQL drivers.
-     * 
-     * @param name the name of the parameter.
-     * @return this execution object to provide the fluent style.
-     * @throws SQLException throw if the parameter is not found, already bound, or there is an issue binding null.
-     */
-    @Override
-    public BatchExecutor bindNull(final String name) throws SQLException {
-        return bindNull(name, Types.VARCHAR, false);
-    }
-
-    /**
-     * Binds null to a parameter, specifying the parameter's type.
-     * 
-     * @param name the name of the parameter.
-     * @param sqlType the type of the parameter.
-     * @return this execution object to provide the fluent style.
-     * @throws SQLException throw if the parameter is not found, already bound, or there is an issue binding null.
-     */
-    @Override
-    public BatchExecutor bindNull(final String name, final int sqlType) throws SQLException {
-        return bindNull(name, sqlType, false);
-    }
-
-    /**
      * Adds the statement to the batch after binding all of the parameters.
      * 
      * @return this object.
@@ -101,6 +74,11 @@ public class BatchExecutor extends AbstractExecutor<BatchExecutor> {
      * @see java.sql.PreparedStatement#addBatch()
      */
     public BatchExecutor addBatch() throws SQLException {
+        // throw an exception if there are unmapped parameters
+        this.throwIfUnmappedParams();
+
+        addBatchCalled = true;
+
         try {
             getStatement().addBatch();
             clearValueMap();
@@ -119,8 +97,9 @@ public class BatchExecutor extends AbstractExecutor<BatchExecutor> {
      * @see org.apache.commons.dbutils2.UpdateExecutor#execute()
      */
     public int[] execute() throws SQLException {
-        // throw an exception if there are unmapped parameters
-        this.throwIfUnmappedParams();
+        if(!addBatchCalled) {
+            throw new SQLException("addBatch must be called before execute.");
+        }
 
         try {
             return getStatement().executeBatch();
diff --git a/src/main/java/org/apache/commons/dbutils2/BatchInsertExecutor.java b/src/main/java/org/apache/commons/dbutils2/BatchInsertExecutor.java
index 01f75d7..94e94b9 100644
--- a/src/main/java/org/apache/commons/dbutils2/BatchInsertExecutor.java
+++ b/src/main/java/org/apache/commons/dbutils2/BatchInsertExecutor.java
@@ -3,6 +3,7 @@ package org.apache.commons.dbutils2;
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 
 
 public class BatchInsertExecutor extends BatchExecutor {
@@ -16,7 +17,7 @@ public class BatchInsertExecutor extends BatchExecutor {
      * @throws SQLException thrown if there is an error during execution.
      */
     BatchInsertExecutor(final Connection conn, final String sql, final boolean closeConnection) throws SQLException {
-        super(conn, sql, closeConnection);
+        super(conn, sql, closeConnection, Statement.RETURN_GENERATED_KEYS);
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/dbutils2/BeanProcessor.java b/src/main/java/org/apache/commons/dbutils2/BeanProcessor.java
index 41f747a..4536e78 100644
--- a/src/main/java/org/apache/commons/dbutils2/BeanProcessor.java
+++ b/src/main/java/org/apache/commons/dbutils2/BeanProcessor.java
@@ -248,13 +248,14 @@ public class BeanProcessor {
     private void callSetter(Object target, PropertyDescriptor prop, Object value)
             throws SQLException {
 
-        Method setter = prop.getWriteMethod();
+        final Method setter = prop.getWriteMethod();
 
         if (setter == null) {
             return;
         }
 
-        Class<?>[] params = setter.getParameterTypes();
+        final Class<?>[] params = setter.getParameterTypes();
+
         try {
             // convert types for some popular ones
             if (value instanceof java.util.Date) {
@@ -270,8 +271,18 @@ public class BeanProcessor {
                 }
             }
 
-            // Don't call setter if the value object isn't the right type
-            if (this.isCompatibleType(value, params[0])) {
+            // see if we can make it work with an enum
+
+            if(params[0].isEnum() && value != null) {
+                try {
+                    final Class cz = Class.forName(params[0].getName());
+                    setter.invoke(target, Enum.valueOf(cz, (String) value));
+                } catch(final ClassNotFoundException e) {
+                    throw new SQLException("Attempted to set an Enum, but class "
+                            + params[0].getName() + " was not found: " + e.getMessage());
+                }
+            } else if (this.isCompatibleType(value, params[0])) {
+                // Don't call setter if the value object isn't the right type
                 setter.invoke(target, new Object[]{value});
             } else {
               throw new SQLException(
diff --git a/src/main/java/org/apache/commons/dbutils2/EntityUtils.java b/src/main/java/org/apache/commons/dbutils2/EntityUtils.java
new file mode 100644
index 0000000..df1a272
--- /dev/null
+++ b/src/main/java/org/apache/commons/dbutils2/EntityUtils.java
@@ -0,0 +1,251 @@
+package org.apache.commons.dbutils2;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.apache.commons.beanutils.PropertyUtils;
+
+
+public class EntityUtils {
+
+    // static methods only
+    private EntityUtils() {
+    }
+
+    /**
+     * Given an entity, returns the table name for the entity.
+     * @param entity the entity to lookup.
+     * @return the name of the table for the entity.
+     */
+    public static String getTableName(final Class<?> entity) {
+        final Entity annotation = entity.getAnnotation(Entity.class);
+
+        if(annotation == null) {
+            throw new IllegalArgumentException(entity.getName() + " does not have the Entity annotation");
+        }
+
+        final Table table = entity.getAnnotation(Table.class);
+
+        // get the table's name from the annotation
+        if(table != null && !table.name().isEmpty()) {
+            return table.name();
+        } else {
+            return entity.getSimpleName();
+        }
+    }
+
+    /**
+     * Given an entity, gets the @Id of the entity, assuming only one ID column.
+     * @param entityClass the type of the entity.
+     * @param entity the instance of the entity.
+     * @return the value of the ID.
+     */
+    @SuppressWarnings("unchecked")
+    public static <T, I> I getId(final Class<T> entityClass, T entity) {
+        Map<String, String> idColumns = getIdColumns(entityClass);
+
+        if(idColumns.size() != 1) {
+            throw new IllegalArgumentException("Cannot get ID for this entity, wrong number of IDs: " + idColumns.size());
+        }
+
+        try {
+            final String column = idColumns.keySet().toArray(new String[0])[0];
+            return (I) PropertyUtils.getSimpleProperty(entity, idColumns.get(column));
+        } catch (IllegalAccessException e) {
+            throw new IllegalArgumentException(e);
+        } catch (InvocationTargetException e) {
+            throw new IllegalArgumentException(e);
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    /**
+     * Gets the names of the columns for a given entity, except those marked as @GeneratedValue.
+     * @param entity the entity to search.
+     * @return a map which contains column name, and field name.
+     */
+    static Map<String, String> getColumns(Class<?> entityClass) {
+        return getColumns(entityClass, false);
+    }
+
+    /**
+     * Gets the names of the columns for a given entity, except those marked as @GeneratedValue.
+     * @param entity the entity to search.
+     * @param excludeIds excludes columns marked with @Id when true
+     * @return a map which contains column name, and field name.
+     */
+    static Map<String, String> getColumns(Class<?> entityClass, boolean excludeIds) {
+        final Map<String, String> ret = new HashMap<String, String>();
+
+        if(entityClass.getAnnotation(Entity.class) == null) {
+            throw new IllegalArgumentException(entityClass.getName() + " does not have the Entity annotation");
+        }
+
+        // we need to walk up the inheritance chain
+        while(entityClass != null) {
+            for(Field field:entityClass.getDeclaredFields()) {
+                final Column column = field.getAnnotation(Column.class);
+                final Id id = field.getAnnotation(Id.class);
+                final GeneratedValue gen = field.getAnnotation(GeneratedValue.class);
+
+                // if we only want IDs, and this isn't an @Id, then skip
+                if(excludeIds && id != null) {
+                    continue;
+                // if we want all columns, then must be marked as a column and not auto-generated
+                } else if(column == null || gen != null) {
+                    continue;
+                }
+
+                String columnName;
+
+                // get the column name or field name
+                if(column.name().isEmpty()) {
+                    columnName = field.getName();
+                } else {
+                    columnName = column.name();
+                }
+
+                if(ret.put(columnName, field.getName()) != null) {
+                    throw new IllegalArgumentException("Entity contains two columns with the same name: " + columnName);
+                }
+            }
+
+            // walk up the inheritance class
+            entityClass = entityClass.getSuperclass();
+        }
+
+        if(ret.isEmpty()) {
+            throw new IllegalArgumentException("Entity does not contain any columns");
+        }
+
+        return ret;
+    }
+
+    /**
+     * Gets the names of the columns that are marked with @Id.
+     * @param entity the entity to search.
+     * @return a map which contains column name, and field name.
+     */
+    public static Map<String, String> getIdColumns(Class<?> entityClass) {
+        final Map<String, String> ret = new HashMap<String, String>();
+
+        if(entityClass.getAnnotation(Entity.class) == null) {
+            throw new IllegalArgumentException(entityClass.getName() + " does not have the Entity annotation");
+        }
+
+        // we need to walk up the inheritance chain
+        while(entityClass != null) {
+            for(Field field:entityClass.getDeclaredFields()) {
+                final Column column = field.getAnnotation(Column.class);
+                final Id id = field.getAnnotation(Id.class);
+
+                // if we only want IDs, and this isn't an @Id, then skip
+                if(column == null || id == null) {
+                    continue;
+                }
+
+                String columnName;
+
+                // get the column name or field name
+                if(column.name().isEmpty()) {
+                    columnName = field.getName();
+                } else {
+                    columnName = column.name();
+                }
+
+                if(ret.put(columnName, field.getName()) != null) {
+                    throw new IllegalArgumentException("Entity contains two columns with the same name: " + columnName);
+                }
+            }
+
+            // walk up the inheritance class
+            entityClass = entityClass.getSuperclass();
+        }
+
+        if(ret.isEmpty()) {
+            throw new IllegalArgumentException("Entity does not contain any columns");
+        }
+
+        return ret;
+    }
+
+    /**
+     * Takes a set of strings (columns) and joins them with commas and a possible prefix.
+     * @param columns the set of columns.
+     * @param prefix a prefix.
+     * @return the joined columns.
+     */
+    static String joinColumnsWithComma(final Set<String> columns, final String prefix) {
+        final StringBuilder sb = new StringBuilder();
+
+        if(columns.isEmpty()) {
+            throw new IllegalArgumentException("Entity does not contain any columns");
+        }
+
+        final Iterator<String> it = columns.iterator();
+
+        if(prefix != null) {
+            sb.append(prefix);
+        }
+
+        sb.append(it.next());
+
+        while(it.hasNext()) {
+            sb.append(",");
+
+            if(prefix != null) {
+                sb.append(prefix);
+            }
+
+            sb.append(it.next());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Takes a set of strings (columns) and joins them with equals and a delimiter.
+     * @param columns the columns to join.
+     * @param delimiter the delimiter between the pairs.
+     * @return the joined columns.
+     */
+    static String joinColumnsEquals(final Set<String> columns, final String delimiter) {
+        final StringBuilder sb = new StringBuilder();
+
+        if(columns.isEmpty()) {
+            throw new IllegalArgumentException("Entity does not contain any columns");
+        }
+
+        final Iterator<String> it = columns.iterator();
+
+        String column = it.next();
+
+        sb.append(column);
+        sb.append(" = :");
+        sb.append(column);
+
+        while(it.hasNext()) {
+            sb.append(delimiter);
+
+            column = it.next();
+
+            sb.append(column);
+            sb.append(" = :");
+            sb.append(column);
+        }
+
+        return sb.toString();
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/dbutils2/InsertExecutor.java b/src/main/java/org/apache/commons/dbutils2/InsertExecutor.java
index 8fdd9db..2fc5a34 100644
--- a/src/main/java/org/apache/commons/dbutils2/InsertExecutor.java
+++ b/src/main/java/org/apache/commons/dbutils2/InsertExecutor.java
@@ -19,6 +19,7 @@ package org.apache.commons.dbutils2;
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 
 
 /**
@@ -39,7 +40,7 @@ public class InsertExecutor extends AbstractExecutor<InsertExecutor> {
      * @throws SQLException thrown if there is an error during execution.
      */
     InsertExecutor(final Connection conn, final String sql, final boolean closeConnection) throws SQLException {
-        super(conn, sql);
+        super(conn, sql, Statement.RETURN_GENERATED_KEYS);
         this.closeConn = closeConnection;
     }
 
@@ -99,7 +100,12 @@ public class InsertExecutor extends AbstractExecutor<InsertExecutor> {
 
         try {
             // execute the insert
-            return getStatement().executeUpdate();
+            int ret = getStatement().executeUpdate();
+
+            // get any generated keys, and just close the ResultSet
+            getStatement().getGeneratedKeys().close();
+
+            return ret;
         } catch (SQLException e) {
             this.rethrow(e);
         } finally {
diff --git a/src/main/java/org/apache/commons/dbutils2/QueryRunner.java b/src/main/java/org/apache/commons/dbutils2/QueryRunner.java
index 2d5fe5c..8294fcf 100644
--- a/src/main/java/org/apache/commons/dbutils2/QueryRunner.java
+++ b/src/main/java/org/apache/commons/dbutils2/QueryRunner.java
@@ -16,10 +16,24 @@
  */
 package org.apache.commons.dbutils2;
 
+import java.lang.reflect.InvocationTargetException;
 import java.sql.Connection;
 import java.sql.SQLException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.persistence.Entity;
 import javax.sql.DataSource;
 
+import org.apache.commons.beanutils.PropertyUtils;
+
+import org.apache.commons.dbutils2.handlers.BeanHandler;
+import org.apache.commons.dbutils2.handlers.BeanListHandler;
+
 /**
  * Executes SQL queries with pluggable strategies for handling
  * <code>ResultSet</code>s.  This class is thread safe.
@@ -312,4 +326,263 @@ public class QueryRunner {
 
         return new InsertExecutor(conn, sql, closeConn);
     }
+
+    //
+    // Entity methods
+    //
+
+    /**
+     * Creates a new entity in the database by calling insert.
+     * @param entity the entity to insert.
+     * @throws SQLException if there is a problem inserting the entity.
+     */
+    public <T> void create(final Class<? extends T> entityClass, final T entity) throws SQLException {
+        internalEntityCreate(entityClass, entity, new HashSet<String>()).execute();
+    }
+
+    /*
+     * Internal method that returns the InsertExecutor making it easier to extend.
+     */
+    protected <T> InsertExecutor internalEntityCreate(final Class<? extends T> entityClass,
+                                                      final T entity,
+                                                      final Set<String> excludeColumns) throws SQLException {
+        final String tableName = EntityUtils.getTableName(entity.getClass());
+        final Map<String, String> columns = EntityUtils.getColumns(entityClass);
+
+        final StringBuilder sb = new StringBuilder("insert into ");
+
+        // create the SQL command
+        sb.append(tableName);
+        sb.append(" (");
+        sb.append(EntityUtils.joinColumnsWithComma(columns.keySet(), null));
+        sb.append(") values(");
+        sb.append(EntityUtils.joinColumnsWithComma(columns.keySet(), ":"));
+        sb.append(")");
+
+        // create the executor
+        final InsertExecutor exec = new InsertExecutor(this.prepareConnection(), sb.toString(), true);
+
+        for(String column:columns.keySet()) {
+            // don't bind the exclude columns
+            if(excludeColumns.contains(column)) {
+                continue;
+            }
+
+            try {
+                // bind all of the values
+                final Object value = PropertyUtils.getSimpleProperty(entity, columns.get(column));
+
+                if(value == null) {
+                    exec.bindNull(column);
+                } else {
+                    exec.bind(column, value);
+                }
+            } catch (final IllegalAccessException e) {
+                throw new SQLException(e);
+            } catch (final InvocationTargetException e) {
+                throw new SQLException(e);
+            } catch (final NoSuchMethodException e) {
+                throw new SQLException(e);
+            }
+        }
+
+        return exec;
+    }
+
+    /**
+     * Reads all of the entities of a given type.
+     * @param entity an entity marked with the {@link Entity} annotation.
+     * @return a list of the entities read .
+     * @throws SQLException If there are database or parameter errors.
+     */
+    public <T> List<T> read(final Class<T> entityClass) throws SQLException {
+        final Entity annotation = entityClass.getAnnotation(Entity.class);
+
+        if(annotation == null) {
+            throw new IllegalArgumentException(entityClass.getName() + " does not have the Entity annotation");
+        }
+
+        // get the table's name
+        final String tableName = EntityUtils.getTableName(entityClass);
+
+        final StringBuilder sb = new StringBuilder("select * from ");
+
+        sb.append(tableName);
+
+        // setup the QueryExecutor
+        final QueryExecutor exec = new QueryExecutor(prepareConnection(), sb.toString(), true);
+
+        // execute using the BeanHandler
+        return exec.execute(new BeanListHandler<T>(entityClass));
+    }
+
+    /**
+     * Reads a given entity based off the @Id columns.
+     * @param entityClass an entity marked with the {@link Entity} annotation.
+     * @param entity the entity to read.
+     * @return the entity read from the db.
+     * @throws SQLException If there are database or parameter errors.
+     */
+    public <T> T read(final Class<T> entityClass, final T entity) throws SQLException {
+        final Entity annotation = entityClass.getAnnotation(Entity.class);
+        final Map<String, String> idColumns = EntityUtils.getIdColumns(entityClass);
+
+        if(annotation == null) {
+            throw new IllegalArgumentException(entityClass.getName() + " does not have the Entity annotation");
+        }
+
+        if(idColumns.isEmpty()) {
+            throw new SQLException("Cannot read " + entityClass.getName() + " because it does not have any @Id columns");
+        }
+
+        // get the table's name
+        final String tableName = EntityUtils.getTableName(entityClass);
+
+        final StringBuilder sb = new StringBuilder("select * from ");
+
+        sb.append(tableName);
+        sb.append(" where ");
+        sb.append(EntityUtils.joinColumnsEquals(idColumns.keySet(), " and "));
+
+        // setup the QueryExecutor
+        final QueryExecutor exec = new QueryExecutor(prepareConnection(), sb.toString(), true);
+
+        // bind all the id columns
+        bindColumnValues(exec, idColumns, entity, Collections.<String>emptySet());
+
+        // execute using the BeanHandler
+        return exec.execute(new BeanHandler<T>(entityClass));
+    }
+
+    /**
+     * Constructs an {@link UpdateEntityExecutor} used to update entities.
+     * @param entity an entity marked with the {@link Entity} annotation.
+     * @return a {@link UpdateEntityExecutor} used to update entities.
+     * @throws SQLException If there are database or parameter errors.
+     */
+    public <T> int update(final Class<T> classType, final T entity) throws SQLException {
+        return update(classType, entity, Collections.<String>emptySet());
+    }
+
+    /**
+     * Constructs an {@link UpdateEntityExecutor} used to update entities that excludes columns during binding.
+     * @param entity an entity marked with the {@link Entity} annotation.
+     * @param excludeColumns a collection of columns to exclude.
+     * @return a {@link UpdateEntityExecutor} used to update entities.
+     * @throws SQLException If there are database or parameter errors.
+     */
+    public <T> int update(final Class<T> entityClass, final T entity, final Collection<String> excludeColumns) throws SQLException {
+        final Map<String, String> updateColumns = EntityUtils.getColumns(entityClass, true);
+        final Map<String, String> idColumns = EntityUtils.getIdColumns(entityClass);
+        final Entity annotation = entityClass.getAnnotation(Entity.class);
+
+        if(annotation == null) {
+            throw new IllegalArgumentException(entityClass.getName() + " does not have the Entity annotation");
+        }
+
+        if(idColumns.isEmpty()) {
+            throw new SQLException("Cannot update " + entityClass.getName() + " because it does not have any @Id columns");
+        }
+
+        // get the table's name
+        final String tableName = EntityUtils.getTableName(entityClass);
+
+        final StringBuilder sb = new StringBuilder("update ");
+
+        // create the SQL command
+        sb.append(tableName);
+        sb.append(" set ");
+        sb.append(EntityUtils.joinColumnsEquals(updateColumns.keySet(), ", "));
+
+        sb.append(" where ");
+        sb.append(EntityUtils.joinColumnsEquals(idColumns.keySet(), " and "));
+
+        // setup the QueryExecutor
+        final UpdateExecutor exec = new UpdateExecutor(prepareConnection(), sb.toString(), true);
+
+        // bind all the update column values
+        bindColumnValues(exec, updateColumns, entity, excludeColumns);
+
+        // bind all the id columns
+        bindColumnValues(exec, idColumns, entity, Collections.<String>emptySet());
+
+        // execute using the BeanHandler
+        return exec.execute();
+    }
+
+    /**
+     * Constructs an {@link DeleteEntityExecutor} used to delete entities.
+     * @param entity an entity marked with the {@link Entity} annotation.
+     * @return a {@link DeleteEntityExecutor} used to delete entities.
+     * @throws SQLException If there are database or parameter errors.
+     */
+    public <T> int delete(final Class<T> entityClass, final T entity) throws SQLException {
+        final Map<String, String> idColumns = EntityUtils.getIdColumns(entityClass);
+        final Entity annotation = entityClass.getAnnotation(Entity.class);
+
+        if(annotation == null) {
+            throw new IllegalArgumentException(entityClass.getName() + " does not have the Entity annotation");
+        }
+
+        if(idColumns.isEmpty()) {
+            throw new SQLException("Cannot update " + entityClass.getName() + " because it does not have any @Id columns");
+        }
+
+        // get the table's name
+        final String tableName = EntityUtils.getTableName(entityClass);
+
+        final StringBuilder sb = new StringBuilder("delete from ");
+
+        sb.append(tableName);
+
+        sb.append(" where ");
+        sb.append(EntityUtils.joinColumnsEquals(idColumns.keySet(), " and "));
+
+        // setup the QueryExecutor
+        final UpdateExecutor exec = new UpdateExecutor(prepareConnection(), sb.toString(), true);
+
+        // bind all the id columns
+        bindColumnValues(exec, idColumns, entity, Collections.<String>emptySet());
+
+        // execute using the BeanHandler
+        return exec.execute();
+    }
+
+    /**
+     * Binds values to an executor.
+     * @param exec
+     * @param columns
+     * @param entity
+     * @param excludes
+     * @throws SQLException
+     */
+    private <T> void bindColumnValues(final AbstractExecutor<?> exec,
+                                      final Map<String, String> columns,
+                                      final T entity,
+                                      final Collection<String> excludes) throws SQLException {
+        for(String column:columns.keySet()) {
+            // skip anything in the exclude set
+            if(excludes.contains(column)) {
+                continue;
+            }
+
+            try {
+                // bind all of the values
+                final Object value = PropertyUtils.getSimpleProperty(entity, columns.get(column));
+
+                if(value == null) {
+                    exec.bindNull(column);
+                } else {
+                    exec.bind(column, value);
+                }
+            } catch (final IllegalAccessException e) {
+                throw new SQLException(e);
+            } catch (final InvocationTargetException e) {
+                throw new SQLException(e);
+            } catch (final NoSuchMethodException e) {
+                throw new SQLException(e);
+            }
+        }
+    }
+
 }
diff --git a/src/test/java/org/apache/commons/dbutils2/AbstractExecutorTest.java b/src/test/java/org/apache/commons/dbutils2/AbstractExecutorTest.java
index ff8e6d1..6ccc6c6 100644
--- a/src/test/java/org/apache/commons/dbutils2/AbstractExecutorTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/AbstractExecutorTest.java
@@ -21,12 +21,13 @@ import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.sql.Types;
 
-import org.apache.commons.dbutils2.AbstractExecutor;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -46,6 +47,7 @@ public class AbstractExecutorTest {
         MockitoAnnotations.initMocks(this);
         
         when(conn.prepareStatement(any(String.class))).thenReturn(stmt);
+        when(conn.prepareStatement(any(String.class), any(Integer.class))).thenReturn(stmt);
     }
     
     @SuppressWarnings("rawtypes")
@@ -57,7 +59,7 @@ public class AbstractExecutorTest {
     public void testGoodSql() throws SQLException {
         createExecutor("select * from blah :first = first and :last=last and phone=:phone");
 
-        verify(conn, times(1)).prepareStatement("select * from blah ? = first and ?=last and phone=?");
+        verify(conn, times(1)).prepareStatement("select * from blah ? = first and ?=last and phone=?", Statement.NO_GENERATED_KEYS);
 
         executor.bind("first", "first_name")
                 .bind(":last", "last_name")
@@ -75,7 +77,7 @@ public class AbstractExecutorTest {
     public void testNoParamsSql() throws SQLException {
         createExecutor("select * from blah");
 
-        verify(conn, times(1)).prepareStatement("select * from blah");
+        verify(conn, times(1)).prepareStatement("select * from blah", Statement.NO_GENERATED_KEYS);
         verify(stmt, times(0)).setObject(any(Integer.class), any(Object.class));
         
         executor.throwIfUnmappedParams();
@@ -85,7 +87,7 @@ public class AbstractExecutorTest {
     public void testMissingParamSql() throws SQLException {
         createExecutor("select * from blah :first = first and :last=last");
 
-        verify(conn, times(1)).prepareStatement("select * from blah ? = first and ?=last");
+        verify(conn, times(1)).prepareStatement("select * from blah ? = first and ?=last", Statement.NO_GENERATED_KEYS);
 
         executor.bind("first", "first_name")
                 .bind(":last", "last_name")
@@ -100,7 +102,7 @@ public class AbstractExecutorTest {
     public void testDoubleBind() throws SQLException {
         createExecutor("select * from blah :first = first and :last=last");
 
-        verify(conn, times(1)).prepareStatement("select * from blah ? = first and ?=last");
+        verify(conn, times(1)).prepareStatement("select * from blah ? = first and ?=last", Statement.NO_GENERATED_KEYS);
 
         executor.bind("first", "first_name")
                 .bind(":last", "last_name")
@@ -114,7 +116,7 @@ public class AbstractExecutorTest {
     public void testNullBind() throws SQLException {
         createExecutor("select * from blah :first = first and :last=last");
 
-        verify(conn, times(1)).prepareStatement("select * from blah ? = first and ?=last");
+        verify(conn, times(1)).prepareStatement("select * from blah ? = first and ?=last", Statement.NO_GENERATED_KEYS);
 
         executor.bindNull("first")
                 .bindNull(":last", Types.NULL);
diff --git a/src/test/java/org/apache/commons/dbutils2/AsyncExecutorTest.java b/src/test/java/org/apache/commons/dbutils2/AsyncExecutorTest.java
index 85657c3..62fd77f 100644
--- a/src/test/java/org/apache/commons/dbutils2/AsyncExecutorTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/AsyncExecutorTest.java
@@ -19,16 +19,11 @@ package org.apache.commons.dbutils2;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+
 import java.sql.SQLException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
 
-import org.apache.commons.dbutils2.AsyncExecutor;
-import org.apache.commons.dbutils2.InsertExecutor;
-import org.apache.commons.dbutils2.QueryExecutor;
-import org.apache.commons.dbutils2.QueryRunner;
-import org.apache.commons.dbutils2.ResultSetHandler;
-import org.apache.commons.dbutils2.UpdateExecutor;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
diff --git a/src/test/java/org/apache/commons/dbutils2/BaseResultSetHandlerTestCase.java b/src/test/java/org/apache/commons/dbutils2/BaseResultSetHandlerTestCase.java
index f9cd433..c389c11 100644
--- a/src/test/java/org/apache/commons/dbutils2/BaseResultSetHandlerTestCase.java
+++ b/src/test/java/org/apache/commons/dbutils2/BaseResultSetHandlerTestCase.java
@@ -22,7 +22,6 @@ import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Map;
 
-import org.apache.commons.dbutils2.BaseResultSetHandler;
 import org.junit.Test;
 
 public final class BaseResultSetHandlerTestCase extends BaseTestCase {
diff --git a/src/test/java/org/apache/commons/dbutils2/BaseTestCase.java b/src/test/java/org/apache/commons/dbutils2/BaseTestCase.java
index 0debc1f..d588067 100644
--- a/src/test/java/org/apache/commons/dbutils2/BaseTestCase.java
+++ b/src/test/java/org/apache/commons/dbutils2/BaseTestCase.java
@@ -40,7 +40,8 @@ public class BaseTestCase extends TestCase {
             "nullObjectTest",
             "nullPrimitiveTest",
             "notDate",
-            "columnProcessorDoubleTest" };
+            "columnProcessorDoubleTest",
+            "enumTest"};
 
     /**
      * The number of columns in the MockResultSet.
@@ -61,7 +62,8 @@ public class BaseTestCase extends TestCase {
             null,
             null,
             new Date(),
-            BigInteger.valueOf(13)};
+            BigInteger.valueOf(13),
+            "ENUM_ZERO"};
 
     private static final Object[] row2 =
         new Object[] {
@@ -74,7 +76,8 @@ public class BaseTestCase extends TestCase {
             null,
             null,
             new Date(),
-            BigInteger.valueOf(13)};
+            BigInteger.valueOf(13),
+            "ENUM_ONE"};
 
     private static final Object[][] rows = new Object[][] { row1, row2 };
 
diff --git a/src/test/java/org/apache/commons/dbutils2/BasicRowProcessorTest.java b/src/test/java/org/apache/commons/dbutils2/BasicRowProcessorTest.java
index 684e9db..4767604 100644
--- a/src/test/java/org/apache/commons/dbutils2/BasicRowProcessorTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/BasicRowProcessorTest.java
@@ -24,9 +24,6 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
-import org.apache.commons.dbutils2.BasicRowProcessor;
-import org.apache.commons.dbutils2.RowProcessor;
-
 /**
  * Test the BasicRowProcessor class.
  */
diff --git a/src/test/java/org/apache/commons/dbutils2/BatchExecutorTest.java b/src/test/java/org/apache/commons/dbutils2/BatchExecutorTest.java
index 1ad3cd2..d8afa00 100644
--- a/src/test/java/org/apache/commons/dbutils2/BatchExecutorTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/BatchExecutorTest.java
@@ -16,16 +16,16 @@
  */
 package org.apache.commons.dbutils2;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
 
-import org.apache.commons.dbutils2.BatchExecutor;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -42,8 +42,8 @@ public class BatchExecutorTest {
     @Before
     public void setup() throws SQLException {
         MockitoAnnotations.initMocks(this);
-        
-        when(conn.prepareStatement(any(String.class))).thenReturn(stmt);
+
+        when(conn.prepareStatement(any(String.class), any(Integer.class))).thenReturn(stmt);
         when(stmt.executeBatch()).thenReturn(new int[] { 2, 3, 4 });
     }
     
@@ -53,8 +53,24 @@ public class BatchExecutorTest {
     
     @Test
     public void testGoodSQL() throws Exception {
-        createExecutor("insert into blah");
-        
+        createExecutor("insert into blah where a = :a and b = :b");
+
+        executor.bind("a", "a").bind("b", "b").addBatch();
+        int[] ret = executor.execute();
+
+        assertEquals(3, ret.length);
+        assertEquals(2, ret[0]);
+        assertEquals(3, ret[1]);
+        assertEquals(4, ret[2]);
+        verify(conn, times(1)).close();
+        verify(stmt, times(1)).close();
+    }
+
+    @Test(expected=SQLException.class)
+    public void testNoBinds() throws Exception {
+        createExecutor("insert into blah where a = :a and b = :b");
+
+        // no bindings done
         executor.addBatch();
         int[] ret = executor.execute();
         
@@ -65,5 +81,36 @@ public class BatchExecutorTest {
         verify(conn, times(1)).close();
         verify(stmt, times(1)).close();
     }
-    
+
+    @Test(expected=SQLException.class)
+    public void testNoAddBatch() throws Exception {
+        createExecutor("insert into blah where a = :a and b = :b");
+
+        // never called addBatch
+        int[] ret = executor.execute();
+
+        assertEquals(3, ret.length);
+        assertEquals(2, ret[0]);
+        assertEquals(3, ret[1]);
+        assertEquals(4, ret[2]);
+        verify(conn, times(1)).close();
+        verify(stmt, times(1)).close();
+    }
+
+    @Test(expected=SQLException.class)
+    public void testNotAllBound() throws Exception {
+        createExecutor("insert into blah where a = :a and b = :b");
+
+        // bind only a
+        executor.bind("a", "a").addBatch();
+        int[] ret = executor.execute();
+
+        assertEquals(3, ret.length);
+        assertEquals(2, ret[0]);
+        assertEquals(3, ret[1]);
+        assertEquals(4, ret[2]);
+        verify(conn, times(1)).close();
+        verify(stmt, times(1)).close();
+    }
+
 }
diff --git a/src/test/java/org/apache/commons/dbutils2/BatchInsertExecutorTest.java b/src/test/java/org/apache/commons/dbutils2/BatchInsertExecutorTest.java
index 919374e..a9e341f 100644
--- a/src/test/java/org/apache/commons/dbutils2/BatchInsertExecutorTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/BatchInsertExecutorTest.java
@@ -18,13 +18,17 @@ package org.apache.commons.dbutils2;
 
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -43,8 +47,8 @@ public class BatchInsertExecutorTest {
     @Before
     public void setup() throws SQLException {
         MockitoAnnotations.initMocks(this);
-        
-        when(conn.prepareStatement(any(String.class))).thenReturn(stmt);
+
+        when(conn.prepareStatement(any(String.class), eq(Statement.RETURN_GENERATED_KEYS))).thenReturn(stmt);
         when(stmt.getGeneratedKeys()).thenReturn(resultSet);
         when(handler.handle(any(ResultSet.class))).thenReturn(new Object());
     }
diff --git a/src/test/java/org/apache/commons/dbutils2/BeanProcessorTest.java b/src/test/java/org/apache/commons/dbutils2/BeanProcessorTest.java
index 73fc26b..56044ca 100644
--- a/src/test/java/org/apache/commons/dbutils2/BeanProcessorTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/BeanProcessorTest.java
@@ -23,9 +23,6 @@ import java.sql.SQLException;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.commons.dbutils2.BeanProcessor;
-import org.apache.commons.dbutils2.ProxyFactory;
-
 public class BeanProcessorTest extends BaseTestCase {
 
     private static final BeanProcessor beanProc = new BeanProcessor();
diff --git a/src/test/java/org/apache/commons/dbutils2/DbUtilsTest.java b/src/test/java/org/apache/commons/dbutils2/DbUtilsTest.java
index 7c00f90..b92b3ea 100644
--- a/src/test/java/org/apache/commons/dbutils2/DbUtilsTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/DbUtilsTest.java
@@ -27,7 +27,6 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 
-import org.apache.commons.dbutils2.DbUtils;
 import org.junit.Test;
 
 public class DbUtilsTest {
diff --git a/src/test/java/org/apache/commons/dbutils2/EnumTest.java b/src/test/java/org/apache/commons/dbutils2/EnumTest.java
new file mode 100644
index 0000000..9db5a7d
--- /dev/null
+++ b/src/test/java/org/apache/commons/dbutils2/EnumTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 
+ *
+ * Licensed 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.dbutils2;
+
+/**
+ * Test Enum class.
+ */
+public enum EnumTest {
+    /**
+     * Enum for zero.
+     */
+    ENUM_ZERO,
+
+    /**
+     * Enum for EnumTwo.
+     */
+    ENUM_ONE,
+
+    /**
+     * Enum for Three.
+     */
+    ENUM_TWO;
+}
diff --git a/src/test/java/org/apache/commons/dbutils2/GenerousBeanProcessorTest.java b/src/test/java/org/apache/commons/dbutils2/GenerousBeanProcessorTest.java
index 4b96e39..40ea521 100644
--- a/src/test/java/org/apache/commons/dbutils2/GenerousBeanProcessorTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/GenerousBeanProcessorTest.java
@@ -19,10 +19,10 @@ package org.apache.commons.dbutils2;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.Mockito.when;
+
 import java.beans.PropertyDescriptor;
 import java.sql.ResultSetMetaData;
 
-import org.apache.commons.dbutils2.GenerousBeanProcessor;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
diff --git a/src/test/java/org/apache/commons/dbutils2/InsertExecutorTest.java b/src/test/java/org/apache/commons/dbutils2/InsertExecutorTest.java
index 3ebc56c..b86f2ab 100644
--- a/src/test/java/org/apache/commons/dbutils2/InsertExecutorTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/InsertExecutorTest.java
@@ -18,16 +18,17 @@ package org.apache.commons.dbutils2;
 
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 
-import org.apache.commons.dbutils2.InsertExecutor;
-import org.apache.commons.dbutils2.ResultSetHandler;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -41,6 +42,7 @@ public class InsertExecutorTest {
     @Mock private ResultSetHandler<Object> handler;
     @Mock private Connection conn;
     @Mock private PreparedStatement stmt;
+    @Mock private PreparedStatement stmtWithResults;
     @Mock private ResultSet resultSet;
     
     @Before
@@ -48,7 +50,8 @@ public class InsertExecutorTest {
         MockitoAnnotations.initMocks(this);
         
         when(conn.prepareStatement(any(String.class))).thenReturn(stmt);
-        when(stmt.getGeneratedKeys()).thenReturn(resultSet);
+        when(conn.prepareStatement(any(String.class), eq(Statement.RETURN_GENERATED_KEYS))).thenReturn(stmtWithResults);
+        when(stmtWithResults.getGeneratedKeys()).thenReturn(resultSet);
         when(handler.handle(any(ResultSet.class))).thenReturn(new Object());
     }
     
@@ -65,7 +68,8 @@ public class InsertExecutorTest {
         assertNotNull(ret);
         verify(handler, times(1)).handle(resultSet);
         verify(conn, times(1)).close();
-        verify(stmt, times(1)).close();
+        verify(stmt, times(0)).close();
+        verify(stmtWithResults, times(1)).close();
     }
     
     @Test(expected=SQLException.class)
diff --git a/src/test/java/org/apache/commons/dbutils2/MockResultSet.java b/src/test/java/org/apache/commons/dbutils2/MockResultSet.java
index 6944dd1..6acdc31 100644
--- a/src/test/java/org/apache/commons/dbutils2/MockResultSet.java
+++ b/src/test/java/org/apache/commons/dbutils2/MockResultSet.java
@@ -26,8 +26,6 @@ import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
-import org.apache.commons.dbutils2.ProxyFactory;
-
 /**
  * MockResultSet dynamically implements the ResultSet interface.
  */
@@ -220,7 +218,7 @@ public class MockResultSet implements InvocationHandler {
     }
 
     /**
-     * @throws SQLException  
+     * @throws SQLException
      */
     protected ResultSetMetaData getMetaData() throws SQLException {
         return this.metaData;
diff --git a/src/test/java/org/apache/commons/dbutils2/MockResultSetMetaData.java b/src/test/java/org/apache/commons/dbutils2/MockResultSetMetaData.java
index d67a19e..e92d527 100644
--- a/src/test/java/org/apache/commons/dbutils2/MockResultSetMetaData.java
+++ b/src/test/java/org/apache/commons/dbutils2/MockResultSetMetaData.java
@@ -20,8 +20,6 @@ import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.sql.ResultSetMetaData;
 
-import org.apache.commons.dbutils2.ProxyFactory;
-
 /**
  * MockResultSetMetaData dynamically implements the ResultSetMetaData
  * interface.
diff --git a/src/test/java/org/apache/commons/dbutils2/ProxyFactoryTest.java b/src/test/java/org/apache/commons/dbutils2/ProxyFactoryTest.java
index f4125f2..b64a03b 100644
--- a/src/test/java/org/apache/commons/dbutils2/ProxyFactoryTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/ProxyFactoryTest.java
@@ -19,8 +19,6 @@ package org.apache.commons.dbutils2;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 
-import org.apache.commons.dbutils2.ProxyFactory;
-
 /**
  * ProxyFactoryTest performs simple type checking on proxy objects returned
  * from a ProxyFactory.
diff --git a/src/test/java/org/apache/commons/dbutils2/QueryExecutorTest.java b/src/test/java/org/apache/commons/dbutils2/QueryExecutorTest.java
index bf6b760..e0d571b 100644
--- a/src/test/java/org/apache/commons/dbutils2/QueryExecutorTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/QueryExecutorTest.java
@@ -21,13 +21,12 @@ import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 
-import org.apache.commons.dbutils2.QueryExecutor;
-import org.apache.commons.dbutils2.ResultSetHandler;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -48,6 +47,7 @@ public class QueryExecutorTest {
         MockitoAnnotations.initMocks(this);
         
         when(conn.prepareStatement(any(String.class))).thenReturn(stmt);
+        when(conn.prepareStatement(any(String.class), any(Integer.class))).thenReturn(stmt);
         when(stmt.executeQuery()).thenReturn(resultSet);
         when(handler.handle(any(ResultSet.class))).thenReturn(new Object());
     }
diff --git a/src/test/java/org/apache/commons/dbutils2/QueryLoaderTest.java b/src/test/java/org/apache/commons/dbutils2/QueryLoaderTest.java
index ff09a2c..0daf44d 100644
--- a/src/test/java/org/apache/commons/dbutils2/QueryLoaderTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/QueryLoaderTest.java
@@ -19,15 +19,12 @@ package org.apache.commons.dbutils2;
 import java.io.IOException;
 import java.util.Map;
 
-import org.apache.commons.dbutils2.QueryLoader;
-
 /**
  * QueryLoaderTest
  */
 public class QueryLoaderTest extends BaseTestCase {
 
-    private static final String QUERIES =
-        "/org/apache/commons/dbutils/TestQueries.properties";
+    private static final String QUERIES = "/TestQueries.properties";
 
     public void testLoad() throws IOException {
         try {
diff --git a/src/test/java/org/apache/commons/dbutils2/QueryRunnerTest.java b/src/test/java/org/apache/commons/dbutils2/QueryRunnerTest.java
index bc3de75..728a6c7 100644
--- a/src/test/java/org/apache/commons/dbutils2/QueryRunnerTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/QueryRunnerTest.java
@@ -20,11 +20,12 @@ import static org.junit.Assert.assertNotNull;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+
 import java.sql.Connection;
 import java.sql.SQLException;
+
 import javax.sql.DataSource;
 
-import org.apache.commons.dbutils2.QueryRunner;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
diff --git a/src/test/java/org/apache/commons/dbutils2/ResultSetIteratorTest.java b/src/test/java/org/apache/commons/dbutils2/ResultSetIteratorTest.java
index 89070a9..0c5f663 100644
--- a/src/test/java/org/apache/commons/dbutils2/ResultSetIteratorTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/ResultSetIteratorTest.java
@@ -18,8 +18,6 @@ package org.apache.commons.dbutils2;
 
 import java.util.Iterator;
 
-import org.apache.commons.dbutils2.ResultSetIterator;
-
 /**
  * ResultSetIteratorTest
  */
diff --git a/src/test/java/org/apache/commons/dbutils2/TestBean.java b/src/test/java/org/apache/commons/dbutils2/TestBean.java
index fc3f8e6..d16c633 100644
--- a/src/test/java/org/apache/commons/dbutils2/TestBean.java
+++ b/src/test/java/org/apache/commons/dbutils2/TestBean.java
@@ -59,6 +59,13 @@ public class TestBean {
     private double columnProcessorDoubleTest = -1;
 
     /**
+     * the ResultSet will have a string in this column and the
+     * BeanProcessors should convert that to a enum of type EnumTest
+     * and store the value in this property.
+     */
+    private EnumTest enumTest;
+
+    /**
      * Constructor for TestBean.
      */
     public TestBean() {
@@ -145,4 +152,11 @@ public class TestBean {
         columnProcessorDoubleTest = d;
     }
 
+    public EnumTest getEnumTest() {
+        return enumTest;
+    }
+
+    public void setEnumTest(EnumTest enumTest) {
+        this.enumTest = enumTest;
+    }
 }
diff --git a/src/test/java/org/apache/commons/dbutils2/UpdateExecutorTest.java b/src/test/java/org/apache/commons/dbutils2/UpdateExecutorTest.java
index 4b9a400..e493768 100644
--- a/src/test/java/org/apache/commons/dbutils2/UpdateExecutorTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/UpdateExecutorTest.java
@@ -16,16 +16,16 @@
  */
 package org.apache.commons.dbutils2;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
 
-import org.apache.commons.dbutils2.UpdateExecutor;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -43,8 +43,8 @@ public class UpdateExecutorTest {
     @Before
     public void setup() throws SQLException {
         MockitoAnnotations.initMocks(this);
-        
-        when(conn.prepareStatement(any(String.class))).thenReturn(stmt);
+
+        when(conn.prepareStatement(any(String.class), any(Integer.class))).thenReturn(stmt);
         when(stmt.executeUpdate()).thenReturn(20);
     }
     
diff --git a/src/test/java/org/apache/commons/dbutils2/handlers/BeanMapHandlerTest.java b/src/test/java/org/apache/commons/dbutils2/handlers/BeanMapHandlerTest.java
index c14951c..65511bb 100644
--- a/src/test/java/org/apache/commons/dbutils2/handlers/BeanMapHandlerTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/handlers/BeanMapHandlerTest.java
@@ -16,7 +16,8 @@
  */
 package org.apache.commons.dbutils2.handlers;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.mockito.Mockito.when;
 
 import java.sql.ResultSet;
@@ -25,7 +26,6 @@ import java.util.Map;
 
 import org.apache.commons.dbutils2.RowProcessor;
 import org.apache.commons.dbutils2.TestBean;
-import org.apache.commons.dbutils2.handlers.BeanMapHandler;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
diff --git a/src/test/java/org/apache/commons/dbutils2/handlers/ColumnListHandlerTest.java b/src/test/java/org/apache/commons/dbutils2/handlers/ColumnListHandlerTest.java
index 4686e53..dee6612 100644
--- a/src/test/java/org/apache/commons/dbutils2/handlers/ColumnListHandlerTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/handlers/ColumnListHandlerTest.java
@@ -21,7 +21,6 @@ import java.util.List;
 
 import org.apache.commons.dbutils2.BaseTestCase;
 import org.apache.commons.dbutils2.ResultSetHandler;
-import org.apache.commons.dbutils2.handlers.ColumnListHandler;
 
 /**
  * ColumnListHandlerTest
diff --git a/src/test/java/org/apache/commons/dbutils2/handlers/KeyedHandlerTest.java b/src/test/java/org/apache/commons/dbutils2/handlers/KeyedHandlerTest.java
index f0cedc0..403ff7f 100644
--- a/src/test/java/org/apache/commons/dbutils2/handlers/KeyedHandlerTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/handlers/KeyedHandlerTest.java
@@ -22,7 +22,6 @@ import java.util.Map.Entry;
 
 import org.apache.commons.dbutils2.BaseTestCase;
 import org.apache.commons.dbutils2.ResultSetHandler;
-import org.apache.commons.dbutils2.handlers.KeyedHandler;
 
 public class KeyedHandlerTest extends BaseTestCase {
 
diff --git a/src/test/java/org/apache/commons/dbutils2/handlers/MapHandlerTest.java b/src/test/java/org/apache/commons/dbutils2/handlers/MapHandlerTest.java
index d8dbf33..ba850a6 100644
--- a/src/test/java/org/apache/commons/dbutils2/handlers/MapHandlerTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/handlers/MapHandlerTest.java
@@ -21,7 +21,6 @@ import java.util.Map;
 
 import org.apache.commons.dbutils2.BaseTestCase;
 import org.apache.commons.dbutils2.ResultSetHandler;
-import org.apache.commons.dbutils2.handlers.MapHandler;
 
 /**
  * MapHandlerTest
diff --git a/src/test/java/org/apache/commons/dbutils2/handlers/MapListHandlerTest.java b/src/test/java/org/apache/commons/dbutils2/handlers/MapListHandlerTest.java
index d14a7c3..1c2a016 100644
--- a/src/test/java/org/apache/commons/dbutils2/handlers/MapListHandlerTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/handlers/MapListHandlerTest.java
@@ -23,7 +23,6 @@ import java.util.Map;
 
 import org.apache.commons.dbutils2.BaseTestCase;
 import org.apache.commons.dbutils2.ResultSetHandler;
-import org.apache.commons.dbutils2.handlers.MapListHandler;
 
 /**
  * MapListHandlerTest
diff --git a/src/test/java/org/apache/commons/dbutils2/handlers/ScalarHandlerTest.java b/src/test/java/org/apache/commons/dbutils2/handlers/ScalarHandlerTest.java
index f44592f..755c0a3 100644
--- a/src/test/java/org/apache/commons/dbutils2/handlers/ScalarHandlerTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/handlers/ScalarHandlerTest.java
@@ -20,7 +20,6 @@ import java.sql.SQLException;
 
 import org.apache.commons.dbutils2.BaseTestCase;
 import org.apache.commons.dbutils2.ResultSetHandler;
-import org.apache.commons.dbutils2.handlers.ScalarHandler;
 
 public class ScalarHandlerTest extends BaseTestCase {
 
diff --git a/src/test/java/org/apache/commons/dbutils2/wrappers/SqlNullCheckedResultSetTest.java b/src/test/java/org/apache/commons/dbutils2/wrappers/SqlNullCheckedResultSetTest.java
index 406d298..d734d4d 100644
--- a/src/test/java/org/apache/commons/dbutils2/wrappers/SqlNullCheckedResultSetTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/wrappers/SqlNullCheckedResultSetTest.java
@@ -40,7 +40,6 @@ import java.util.Map;
 
 import org.apache.commons.dbutils2.BaseTestCase;
 import org.apache.commons.dbutils2.ProxyFactory;
-import org.apache.commons.dbutils2.wrappers.SqlNullCheckedResultSet;
 
 /**
  * Test cases for <code>SqlNullCheckedResultSet</code> class.
diff --git a/src/test/java/org/apache/commons/dbutils2/wrappers/StringTrimmedResultSetTest.java b/src/test/java/org/apache/commons/dbutils2/wrappers/StringTrimmedResultSetTest.java
index c5de076..ef22663 100644
--- a/src/test/java/org/apache/commons/dbutils2/wrappers/StringTrimmedResultSetTest.java
+++ b/src/test/java/org/apache/commons/dbutils2/wrappers/StringTrimmedResultSetTest.java
@@ -22,8 +22,6 @@ import java.sql.SQLException;
 import org.apache.commons.dbutils2.BaseTestCase;
 import org.apache.commons.dbutils2.MockResultSet;
 import org.apache.commons.dbutils2.ProxyFactory;
-import org.apache.commons.dbutils2.wrappers.SqlNullCheckedResultSet;
-import org.apache.commons.dbutils2.wrappers.StringTrimmedResultSet;
 
 /**
  * StringTrimmedResultSetTest