diff --git a/beehive-jdbc-control/pom.xml b/beehive-jdbc-control/pom.xml
index fdf1292..2e240d3 100644
--- a/beehive-jdbc-control/pom.xml
+++ b/beehive-jdbc-control/pom.xml
@@ -33,6 +33,11 @@
beehive-controls
${project.version}
+
+ com.moparisthebest.beehive
+ beehive-jdbc-mapper
+ ${project.version}
+
com.moparisthebest.aptIn16
netui-compiler
diff --git a/beehive-jdbc-control/src/main/java/org/apache/beehive/controls/system/jdbc/NewDefaultObjectResultSetMapper.java b/beehive-jdbc-control/src/main/java/org/apache/beehive/controls/system/jdbc/NewDefaultObjectResultSetMapper.java
new file mode 100644
index 0000000..807449d
--- /dev/null
+++ b/beehive-jdbc-control/src/main/java/org/apache/beehive/controls/system/jdbc/NewDefaultObjectResultSetMapper.java
@@ -0,0 +1,56 @@
+package org.apache.beehive.controls.system.jdbc;
+
+import org.apache.beehive.controls.api.context.ControlBeanContext;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.sql.ResultSet;
+import java.util.*;
+
+/**
+ * Refer to org.apache.beehive.controls.system.jdbc.ResultSetMapper for how this class operates
+ */
+public class NewDefaultObjectResultSetMapper extends com.moparisthebest.jdbc.ResultSetMapper implements org.apache.beehive.controls.system.jdbc.ResultSetMapper {
+ /**
+ * Map the ResultSet to the method's return type. The object type returned is defined by the return type of the method.
+ *
+ * @param context A ControlBeanContext instance, see Beehive controls javadoc for additional information
+ * @param m Method assoicated with this call.
+ * @param rs Result set to map.
+ * @param cal A Calendar instance for time/date value resolution.
+ * @return The Object resulting from the ResultSet
+ */
+ @SuppressWarnings({"unchecked"})
+ public Object mapToResultType(ControlBeanContext context, Method m, ResultSet rs, Calendar cal) {
+ final Class returnType = m.getReturnType();
+ if (returnType.isArray()) {
+ return toArray(rs, returnType.getComponentType(), context.getMethodPropertySet(m, JdbcControl.SQL.class).arrayMaxLength(), cal);
+ } else if (Collection.class.isAssignableFrom(returnType)) {
+ return toCollection(rs, returnType, (Class) getActualTypeArguments(m)[0], context.getMethodPropertySet(m, JdbcControl.SQL.class).arrayMaxLength(), cal);
+ } else if (Map.class.isAssignableFrom(returnType)) {
+ Type[] types = getActualTypeArguments(m);
+ if (types[1] instanceof ParameterizedType) { // for collectionMaps
+ ParameterizedType pt = (ParameterizedType) types[1];
+ Class collectionType = (Class) pt.getRawType();
+ if (Collection.class.isAssignableFrom(collectionType))
+ return toMapCollection(rs, returnType, (Class) types[0], collectionType, (Class) pt.getActualTypeArguments()[0], context.getMethodPropertySet(m, JdbcControl.SQL.class).arrayMaxLength(), cal);
+ }
+ return toMap(rs, returnType, (Class) types[0], (Class) types[1], context.getMethodPropertySet(m, JdbcControl.SQL.class).arrayMaxLength(), cal);
+ } else if (Iterator.class.isAssignableFrom(returnType)) {
+ return ListIterator.class.isAssignableFrom(returnType) ?
+ toListIterator(rs, (Class) getActualTypeArguments(m)[0], context.getMethodPropertySet(m, JdbcControl.SQL.class).arrayMaxLength(), cal) :
+ toIterator(rs, (Class) getActualTypeArguments(m)[0], context.getMethodPropertySet(m, JdbcControl.SQL.class).arrayMaxLength(), cal);
+ } else {
+ return toObject(rs, returnType, cal);
+ }
+ }
+
+ private static Type[] getActualTypeArguments(Method m) {
+ return ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments();
+ }
+
+ public boolean canCloseResultSet() {
+ return true;
+ }
+}
diff --git a/beehive-jdbc-mapper/genQueryMapper.sh b/beehive-jdbc-mapper/genQueryMapper.sh
new file mode 100755
index 0000000..2ec05a6
--- /dev/null
+++ b/beehive-jdbc-mapper/genQueryMapper.sh
@@ -0,0 +1,117 @@
+#!/bin/bash
+cd "$(dirname "$0")"
+
+function prepareFile(){
+ path="$1"
+ tmp_path="$(basename "$path")"
+ (
+ sed -n '/CODE AUTOMATICALLY GENERATED BY genQueryMapper.sh/q;p' "$path"
+ echo -e '\t// DO NOT EDIT BELOW THIS LINE, OR CHANGE THIS COMMENT, CODE AUTOMATICALLY GENERATED BY genQueryMapper.sh\n'
+ ) > "$tmp_path"
+ echo "$tmp_path"
+}
+
+function finishFile(){
+ path="$1"
+ tmp_path="$(basename "$path")"
+ echo -e "}\n" >> "$tmp_path"
+ mv "$tmp_path" "$path"
+}
+
+
+result="$(prepareFile "src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java")"
+
+# single object types
+cat src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java | grep public | egrep '(toObject|toSingleMap)\(' | grep ', Calendar cal)' | while read method
+do
+ #echo "method: $method"
+ method_name=$(echo $method | egrep -o '[^ ]+\(')
+ echo "ResultSetMapper.$method_name)"
+
+ cat >> "$result" <> "$result" <> "$query" <> "$caching_query" <> "$null_query"
+done
+
+finishFile "src/main/java/com/moparisthebest/jdbc/QueryMapper.java"
+finishFile "src/main/java/com/moparisthebest/jdbc/CachingQueryMapper.java"
+finishFile "src/main/java/com/moparisthebest/jdbc/NullQueryMapper.java"
diff --git a/beehive-jdbc-mapper/pom.xml b/beehive-jdbc-mapper/pom.xml
new file mode 100644
index 0000000..fb295ef
--- /dev/null
+++ b/beehive-jdbc-mapper/pom.xml
@@ -0,0 +1,16 @@
+
+
+ 4.0.0
+
+ com.moparisthebest.beehive
+ beehive
+ 1.0.3-SNAPSHOT
+
+ beehive-jdbc-mapper
+ ${project.artifactId}
+
+ ${project.artifactId}
+
+
diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CachingQueryMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CachingQueryMapper.java
new file mode 100644
index 0000000..6aa893e
--- /dev/null
+++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CachingQueryMapper.java
@@ -0,0 +1,296 @@
+package com.moparisthebest.jdbc;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
+
+import static com.moparisthebest.jdbc.TryClose.tryClose;
+
+/**
+ * This class caches the PreparedStatement's it creates for the strings you send in, then closes them when the close() method is called.
+ * Since PreparedStatement is not thread-safe, this class cannot be either. Be sure to call it from only a single thread
+ * or synchronize around it.
+ */
+public class CachingQueryMapper extends QueryMapper {
+
+ protected final Map cache;
+
+ protected CachingQueryMapper(Connection conn, String jndiName, ResultSetMapper cm, final int maxEntries) {
+ super(conn, jndiName, cm);
+ if (maxEntries > 0) { // we want a limited cache
+ final float loadFactor = 0.75f; // default for HashMaps
+ // if we set the initialCapacity this way, nothing should ever need re-sized
+ final int initialCapacity = ((int) Math.ceil(maxEntries / loadFactor)) + 1;
+ cache = new LinkedHashMap(initialCapacity, loadFactor, true) {
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ final boolean remove = size() > maxEntries;
+ if(remove){
+ //System.out.printf("closing PreparedStatement '%s' with key '%s'\n", eldest.getValue(), eldest.getKey());
+ tryClose(eldest.getValue());
+ }
+ return remove;
+ }
+ };
+ } else
+ cache = new HashMap();
+ }
+
+ public CachingQueryMapper(Connection conn, ResultSetMapper cm, final int maxEntries) {
+ this(conn, null, cm, maxEntries);
+ }
+
+ public CachingQueryMapper(Connection conn, final int maxEntries) {
+ this(conn, null, null, maxEntries);
+ }
+
+ public CachingQueryMapper(String jndiName, ResultSetMapper cm, final int maxEntries) {
+ this(null, jndiName, cm, maxEntries);
+ }
+
+ public CachingQueryMapper(String jndiName, final int maxEntries) {
+ this(null, jndiName, null, maxEntries);
+ }
+
+ protected CachingQueryMapper(Connection conn, String jndiName, ResultSetMapper cm) {
+ this(conn, jndiName, cm, 20); // default size of 20
+ }
+
+ public CachingQueryMapper(Connection conn, ResultSetMapper cm) {
+ this(conn, null, cm);
+ }
+
+ public CachingQueryMapper(Connection conn) {
+ this(conn, null, null);
+ }
+
+ public CachingQueryMapper(String jndiName, ResultSetMapper cm) {
+ this(null, jndiName, cm);
+ }
+
+ public CachingQueryMapper(String jndiName) {
+ this(null, jndiName, null);
+ }
+
+ protected PreparedStatement getPreparedStatement(String sql) throws SQLException {
+ return getPreparedStatement(sql, ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY);
+ }
+
+ protected PreparedStatement getPreparedStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
+ PreparedStatement ps = cache.get(sql);
+ if (ps == null) {
+ //System.out.println("cache miss");
+ ps = conn.prepareStatement(sql,resultSetType,resultSetConcurrency);
+ cache.put(sql, ps);
+ }
+ //else System.out.println("cache hit");
+ return ps;
+ }
+
+ public void clearCache(boolean close) {
+ //System.out.println("cache size: "+cache.size());
+ for (PreparedStatement ps : cache.values())
+ tryClose(ps);
+ if (close)
+ super.close();
+ else
+ cache.clear();
+ }
+
+ public void clearCache() {
+ this.clearCache(false);
+ }
+
+ @Override
+ public void close() {
+ this.clearCache(true);
+ }
+
+ @Override
+ public int executeUpdate(String sql, Object... bindObjects) throws SQLException {
+ return super.executeUpdate(getPreparedStatement(sql), bindObjects);
+ }
+
+ @Override
+ public boolean executeUpdateSuccess(String sql, Object... bindObjects) throws SQLException {
+ return super.executeUpdateSuccess(getPreparedStatement(sql), bindObjects);
+ }
+
+ // these grab ResultSets from the database
+
+ @Override
+ public ResultSet toResultSet(String sql, Object... bindObjects) throws SQLException {
+ return super.toResultSet(getPreparedStatement(sql), bindObjects);
+ }
+
+ @Override
+ public ResultSet toResultSet(String sql, Integer rsType, Integer rsConcurrency, Object... bindObjects) throws SQLException {
+ return super.toResultSet(getPreparedStatement(sql,rsType,rsConcurrency), bindObjects);
+ }
+
+ // DO NOT EDIT BELOW THIS LINE, OR CHANGE THIS COMMENT, CODE AUTOMATICALLY GENERATED BY genQueryMapper.sh
+
+ @Override
+ public T toObject(String sql, Class componentType, final Object... bindObjects) throws SQLException {
+ return super.toObject(getPreparedStatement(sql), componentType, bindObjects);
+ }
+
+ @Override
+ public , V> Map toSingleMap(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException {
+ return super.toSingleMap(getPreparedStatement(sql), componentType, mapValType, bindObjects);
+ }
+
+ @Override
+ public Map toSingleMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException {
+ return super.toSingleMap(getPreparedStatement(sql), mapValType, bindObjects);
+ }
+
+ @Override
+ public , E> T toCollection(String sql, final Class collectionType, Class componentType, final Object... bindObjects) throws SQLException {
+ return super.toCollection(getPreparedStatement(sql), collectionType, componentType, bindObjects);
+ }
+
+ @Override
+ public , E> T toCollection(String sql, T list, Class componentType, final Object... bindObjects) throws SQLException {
+ return super.toCollection(getPreparedStatement(sql), list, componentType, bindObjects);
+ }
+
+ @Override
+ public , K, E> T toMap(String sql, final Class returnType, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException {
+ return super.toMap(getPreparedStatement(sql), returnType, mapKeyType, componentType, bindObjects);
+ }
+
+ @Override
+ public , K, E> T toMap(String sql, T map, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException {
+ return super.toMap(getPreparedStatement(sql), map, mapKeyType, componentType, bindObjects);
+ }
+
+ @Override
+ public , K, E extends Collection, C> T toMapCollection(String sql, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) throws SQLException {
+ return super.toMapCollection(getPreparedStatement(sql), returnType, mapKeyType, collectionType, componentType, bindObjects);
+ }
+
+ @Override
+ public , K, E extends Collection, C> T toMapCollection(String sql, T map, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) throws SQLException {
+ return super.toMapCollection(getPreparedStatement(sql), map, mapKeyType, collectionType, componentType, bindObjects);
+ }
+
+ @Override
+ public ListIterator toListIterator(String sql, final Class type, final Object... bindObjects) throws SQLException {
+ return super.toListIterator(getPreparedStatement(sql), type, bindObjects);
+ }
+
+ @Override
+ public Iterator toIterator(String sql, final Class type, final Object... bindObjects) throws SQLException {
+ return super.toIterator(getPreparedStatement(sql), type, bindObjects);
+ }
+
+ @Override
+ public T[] toArray(String sql, final Class type, final Object... bindObjects) throws SQLException {
+ return super.toArray(getPreparedStatement(sql), type, bindObjects);
+ }
+
+ @Override
+ public List toList(String sql, Class componentType, final Object... bindObjects) throws SQLException {
+ return super.toList(getPreparedStatement(sql), componentType, bindObjects);
+ }
+
+ @Override
+ public Map toMap(String sql, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException {
+ return super.toMap(getPreparedStatement(sql), mapKeyType, componentType, bindObjects);
+ }
+
+ @Override
+ public , C> Map toMapList(String sql, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException {
+ return super.toMapList(getPreparedStatement(sql), mapKeyType, componentType, bindObjects);
+ }
+
+ @Override
+ public , E extends Map, V> T toCollectionMap(String sql, final Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException {
+ return super.toCollectionMap(getPreparedStatement(sql), collectionType, componentType, mapValType, bindObjects);
+ }
+
+ @Override
+ public , E extends Map, V> T toCollectionMap(String sql, T list, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException {
+ return super.toCollectionMap(getPreparedStatement(sql), list, componentType, mapValType, bindObjects);
+ }
+
+ @Override
+ public , K, E extends Map, V> T toMapMap(String sql, final Class returnType, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException {
+ return super.toMapMap(getPreparedStatement(sql), returnType, mapKeyType, componentType, mapValType, bindObjects);
+ }
+
+ @Override
+ public , K, E extends Map, V> T toMapMap(String sql, T map, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException {
+ return super.toMapMap(getPreparedStatement(sql), map, mapKeyType, componentType, mapValType, bindObjects);
+ }
+
+ @Override
+ public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(String sql, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException {
+ return super.toMapCollectionMap(getPreparedStatement(sql), returnType, mapKeyType, collectionType, componentType, mapValType, bindObjects);
+ }
+
+ @Override
+ public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(String sql, T map, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException {
+ return super.toMapCollectionMap(getPreparedStatement(sql), map, mapKeyType, collectionType, componentType, mapValType, bindObjects);
+ }
+
+ @Override
+ public , V> ListIterator