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> toListIteratorMap(String sql, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + return super.toListIteratorMap(getPreparedStatement(sql), type, mapValType, bindObjects); + } + + @Override + public , V> Iterator> toIteratorMap(String sql, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + return super.toIteratorMap(getPreparedStatement(sql), type, mapValType, bindObjects); + } + + @Override + public , V> Map[] toArrayMap(String sql, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + return super.toArrayMap(getPreparedStatement(sql), type, mapValType, bindObjects); + } + + @Override + public , V> List> toListMap(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return super.toListMap(getPreparedStatement(sql), componentType, mapValType, bindObjects); + } + + @Override + public , V> Map> toMapMap(String sql, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return super.toMapMap(getPreparedStatement(sql), mapKeyType, componentType, mapValType, bindObjects); + } + + @Override + public , V> Map>> toMapListMap(String sql, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return super.toMapListMap(getPreparedStatement(sql), mapKeyType, componentType, mapValType, bindObjects); + } + + @Override + public ListIterator> toListIteratorMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { + return super.toListIteratorMap(getPreparedStatement(sql), mapValType, bindObjects); + } + + @Override + public Iterator> toIteratorMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { + return super.toIteratorMap(getPreparedStatement(sql), mapValType, bindObjects); + } + + @Override + public List> toListMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { + return super.toListMap(getPreparedStatement(sql), mapValType, bindObjects); + } + + @Override + public Map> toMapMap(String sql, Class mapKeyType, Class mapValType, final Object... bindObjects) throws SQLException { + return super.toMapMap(getPreparedStatement(sql), mapKeyType, mapValType, bindObjects); + } + + @Override + public Map>> toMapListMap(String sql, Class mapKeyType, Class mapValType, final Object... bindObjects) throws SQLException { + return super.toMapListMap(getPreparedStatement(sql), mapKeyType, mapValType, bindObjects); + } + +} + diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CachingResultSetMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CachingResultSetMapper.java new file mode 100644 index 0000000..792547d --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CachingResultSetMapper.java @@ -0,0 +1,20 @@ +package com.moparisthebest.jdbc; + +import java.sql.ResultSet; +import java.util.Calendar; + +public class CachingResultSetMapper extends ResultSetMapper { + + public CachingResultSetMapper(Calendar cal, int arrayMaxLength) { + super(cal, arrayMaxLength); + } + + public CachingResultSetMapper() { + super(); + } + + @Override + protected RowToObjectMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType) { + return new CachingRowToObjectMapper(resultSet, returnTypeClass, cal, mapValType); + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CachingRowToObjectMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CachingRowToObjectMapper.java new file mode 100644 index 0000000..0f9c27a --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CachingRowToObjectMapper.java @@ -0,0 +1,65 @@ +package com.moparisthebest.jdbc; + +import java.lang.reflect.AccessibleObject; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + +public class CachingRowToObjectMapper extends RowToObjectMapper { + + protected static final Map cache = new HashMap(); + + protected final int thisHash; + protected final String[] keys; + + public CachingRowToObjectMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType) { + super(resultSet, returnTypeClass, cal, mapValType); + try { + keys = super.getKeysFromResultSet(); + //System.out.printf("keys: %d, %s: %d\n", Arrays.hashCode(keys), _returnTypeClass, _returnTypeClass.hashCode()); + thisHash = Arrays.hashCode(keys) ^ _returnTypeClass.hashCode(); + } catch (SQLException e) { + throw new MapperException("CachingRowToObjectMapper: SQLException: " + e.getMessage(), e); + } + } + + @Override + protected String[] getKeysFromResultSet() throws SQLException { + return keys; + } + + @Override + protected void getFieldMappings() throws SQLException { + FieldMapping fm = cache.get(thisHash); + if (fm == null) { + //System.out.printf("cache miss, hashcode: %d\n", thisHash); + // generate and put into cache + super.getFieldMappings(); + synchronized (cache) { + // I *think* we only need to synchronize here, instead of around the get as well + // it may allow some leaks (field mappings being generated more than once) + // but the performance benefits of not having this entire method synchronized + // statically probably outweighs the negatives + cache.put(thisHash, new FieldMapping(_fields, _fieldTypes)); + } + } else { + //System.out.printf("cache hit, hashcode: %d\n", thisHash); + // load from cache + _fields = fm._fields; + _fieldTypes = fm._fieldTypes; + } + } + + private static class FieldMapping { + public final AccessibleObject[] _fields; + public final int[] _fieldTypes; + + private FieldMapping(AccessibleObject[] _fields, int[] _fieldTypes) { + this._fields = _fields; + this._fieldTypes = _fieldTypes; + } + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/Cleaner.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/Cleaner.java new file mode 100644 index 0000000..6db48dd --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/Cleaner.java @@ -0,0 +1,6 @@ +package com.moparisthebest.jdbc; + +public interface Cleaner { + + public T clean(T dto); +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningResultSetMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningResultSetMapper.java new file mode 100644 index 0000000..14f77eb --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningResultSetMapper.java @@ -0,0 +1,25 @@ +package com.moparisthebest.jdbc; + +import java.sql.ResultSet; +import java.util.Calendar; + +public class CleaningResultSetMapper extends ResultSetMapper { + + private final Cleaner cleaner; + + public CleaningResultSetMapper(Cleaner cleaner, Calendar cal, int arrayMaxLength) { + super(cal, arrayMaxLength); + this.cleaner = cleaner; + } + + public CleaningResultSetMapper(Cleaner cleaner) { + super(); + this.cleaner = cleaner; + } + + @Override + @SuppressWarnings({"unchecked"}) + protected RowToObjectMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType) { + return new CleaningRowToObjectMapper((Cleaner)cleaner, resultSet, returnTypeClass, cal, mapValType); + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningRowToObjectMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningRowToObjectMapper.java new file mode 100644 index 0000000..91d3388 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningRowToObjectMapper.java @@ -0,0 +1,21 @@ +package com.moparisthebest.jdbc; + +import java.sql.ResultSet; +import java.util.Calendar; + +public class CleaningRowToObjectMapper extends RowToObjectMapper { + + private final Cleaner cleaner; + + public CleaningRowToObjectMapper(Cleaner cleaner, ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType) { + super(resultSet, returnTypeClass, cal, mapValType); + if (cleaner == null) + throw new NullPointerException("cleaner cannot be null!"); + this.cleaner = cleaner; + } + + @Override + public T mapRowToReturnType() { + return cleaner.clean(super.mapRowToReturnType()); + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/Finishable.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/Finishable.java new file mode 100644 index 0000000..97d2262 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/Finishable.java @@ -0,0 +1,8 @@ +package com.moparisthebest.jdbc; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public interface Finishable { + public void finish(ResultSet rs) throws SQLException; +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/MapperException.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/MapperException.java new file mode 100644 index 0000000..583ab8d --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/MapperException.java @@ -0,0 +1,45 @@ +package com.moparisthebest.jdbc; + +/** + * The MapperException class declares an unchecked exception that is thrown by the Controls + * runtime under certain failure conditions. + */ +public class MapperException extends RuntimeException +{ + /** + * Default constructor. + */ + public MapperException() { + super(); + } + + /** + * Constructs a MapperException object with the specified String as a message. + * + * @param message The message to use. + */ + public MapperException(String message) + { + super(message); + } + + /** + * Constructs a MapperException with the specified cause. + * @param t the cause + */ + public MapperException(Throwable t) { + super(t); + } + + /** + * Constructs a MapperException object using the specified String as a message, and the + * specified Throwable as a nested exception. + * + * @param message The message to use. + * @param t The exception to nest within this exception. + */ + public MapperException(String message, Throwable t) + { + super(message + "[" + t + "]", t); + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/NullQueryMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/NullQueryMapper.java new file mode 100644 index 0000000..5484e01 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/NullQueryMapper.java @@ -0,0 +1,887 @@ +package com.moparisthebest.jdbc; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +public class NullQueryMapper extends QueryMapper { + + protected final boolean verbose; + protected final QueryMapper delegate; + + private NullQueryMapper(Connection conn, String jndiName, QueryMapper delegate, ResultSetMapper cm, boolean verbose) { + this.verbose = verbose; + this.delegate = delegate == null ? new QueryMapper(conn, jndiName, cm) : delegate; + } + + public NullQueryMapper(QueryMapper delegate, boolean verbose) { + this(null, null, delegate, null, verbose); + } + + public NullQueryMapper(QueryMapper delegate) { + this(null, null, delegate, null, true); + } + + public NullQueryMapper(Connection conn, boolean verbose) { + this(conn, null, null, null, verbose); + } + + public NullQueryMapper(Connection conn, ResultSetMapper cm, boolean verbose) { + this(conn, null, null, cm, verbose); + } + + public NullQueryMapper(Connection conn) { + this(conn, true); + } + + public NullQueryMapper(Connection conn, ResultSetMapper cm) { + this(conn, cm, true); + } + + public NullQueryMapper(String jndiName, boolean verbose) { + this(null, jndiName, null, null, verbose); + } + + public NullQueryMapper(String jndiName, ResultSetMapper cm, boolean verbose) { + this(null, jndiName, null, cm, verbose); + } + + public NullQueryMapper(String jndiName) { + this(jndiName, true); + } + + public NullQueryMapper(String jndiName, ResultSetMapper cm) { + this(jndiName, cm, true); + } + + // these update the database + + @Override + public int executeUpdate(PreparedStatement ps, Object... bindObjects) { + try { + return delegate.executeUpdate(ps, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return -1; + } + + @Override + public boolean executeUpdateSuccess(PreparedStatement ps, Object... bindObjects) { + try { + return delegate.executeUpdateSuccess(ps, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return false; + } + + @Override + public int executeUpdate(String sql, Object... bindObjects) { + try { + return delegate.executeUpdate(sql, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return -1; + } + + @Override + public boolean executeUpdateSuccess(String sql, Object... bindObjects) { + try { + return delegate.executeUpdateSuccess(sql, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return false; + } + + // these update the database using UpdateableDTOs + + @Override + public int updateRows(UpdateableDTO dto) { + try { + return delegate.updateRows(dto); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return -1; + } + + @Override + public int updateRows(Collection dtos) { + try { + return delegate.updateRows(dtos); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return -1; + } + + @Override + public int updateRows(UpdateableDTO[] dtos) { + try { + return delegate.updateRows(dtos); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return -1; + } + + @Override + public int insertRows(UpdateableDTO dto) { + try { + return delegate.insertRows(dto); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return -1; + } + + @Override + public int insertRows(Collection dtos) { + try { + return delegate.insertRows(dtos); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return -1; + } + + @Override + public int insertRows(UpdateableDTO[] dtos) { + try { + return delegate.insertRows(dtos); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return -1; + } + + // these grab ResultSets from the database + + @Override + public ResultSet toResultSet(PreparedStatement ps, Object... bindObjects) { + try { + return delegate.toResultSet(ps, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public ResultSet toResultSet(String sql, Object... bindObjects) { + try { + return delegate.toResultSet(sql, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + // these are standard getters + + @Override + public ResultSetMapper getCustomResultSetMapper() { + return delegate.getCustomResultSetMapper(); + } + + @Override + public Connection getConnection() { + return delegate.getConnection(); + } + + // these just delegate and change no functionality + + @Override + public void close() { + delegate.close(); + } + + @Override + protected PreparedStatement bind(PreparedStatement ps, Object... bindObjects) throws SQLException { + return delegate.bind(ps, bindObjects); + } + + @Override + protected ResultSet bindExecute(PreparedStatement ps, Object... bindObjects) throws SQLException { + return delegate.bindExecute(ps, bindObjects); + } + + // and these are standard + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + NullQueryMapper that = (NullQueryMapper) o; + + if (verbose != that.verbose) return false; + if (delegate != null ? !delegate.equals(that.delegate) : that.delegate != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = (verbose ? 1 : 0); + result = 31 * result + (delegate != null ? delegate.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "NullQueryMapper{" + + "verbose=" + verbose + + ", delegate=" + delegate + + "}"; + } + + // DO NOT EDIT BELOW THIS LINE, OR CHANGE THIS COMMENT, CODE AUTOMATICALLY GENERATED BY genQueryMapper.sh + + @Override + public T toObject(PreparedStatement query, Class componentType, final Object... bindObjects) { + try { + return delegate.toObject(query, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public T toObject(String query, Class componentType, final Object... bindObjects) { + try { + return delegate.toObject(query, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> Map toSingleMap(PreparedStatement query, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toSingleMap(query, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> Map toSingleMap(String query, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toSingleMap(query, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public Map toSingleMap(PreparedStatement query, Class mapValType, final Object... bindObjects) { + try { + return delegate.toSingleMap(query, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public Map toSingleMap(String query, Class mapValType, final Object... bindObjects) { + try { + return delegate.toSingleMap(query, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , E> T toCollection(PreparedStatement query, final Class collectionType, Class componentType, final Object... bindObjects) { + try { + return delegate.toCollection(query, collectionType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , E> T toCollection(String query, final Class collectionType, Class componentType, final Object... bindObjects) { + try { + return delegate.toCollection(query, collectionType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , E> T toCollection(PreparedStatement query, T list, Class componentType, final Object... bindObjects) { + try { + return delegate.toCollection(query, list, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , E> T toCollection(String query, T list, Class componentType, final Object... bindObjects) { + try { + return delegate.toCollection(query, list, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, E> T toMap(PreparedStatement query, final Class returnType, Class mapKeyType, Class componentType, final Object... bindObjects) { + try { + return delegate.toMap(query, returnType, mapKeyType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, E> T toMap(String query, final Class returnType, Class mapKeyType, Class componentType, final Object... bindObjects) { + try { + return delegate.toMap(query, returnType, mapKeyType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, E> T toMap(PreparedStatement query, T map, Class mapKeyType, Class componentType, final Object... bindObjects) { + try { + return delegate.toMap(query, map, mapKeyType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, E> T toMap(String query, T map, Class mapKeyType, Class componentType, final Object... bindObjects) { + try { + return delegate.toMap(query, map, mapKeyType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, E extends Collection, C> T toMapCollection(PreparedStatement query, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) { + try { + return delegate.toMapCollection(query, returnType, mapKeyType, collectionType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, E extends Collection, C> T toMapCollection(String query, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) { + try { + return delegate.toMapCollection(query, returnType, mapKeyType, collectionType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, E extends Collection, C> T toMapCollection(PreparedStatement query, T map, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) { + try { + return delegate.toMapCollection(query, map, mapKeyType, collectionType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, E extends Collection, C> T toMapCollection(String query, T map, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) { + try { + return delegate.toMapCollection(query, map, mapKeyType, collectionType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public ListIterator toListIterator(PreparedStatement query, final Class type, final Object... bindObjects) { + try { + return delegate.toListIterator(query, type, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public ListIterator toListIterator(String query, final Class type, final Object... bindObjects) { + try { + return delegate.toListIterator(query, type, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public Iterator toIterator(PreparedStatement query, final Class type, final Object... bindObjects) { + try { + return delegate.toIterator(query, type, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public Iterator toIterator(String query, final Class type, final Object... bindObjects) { + try { + return delegate.toIterator(query, type, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public T[] toArray(PreparedStatement query, final Class type, final Object... bindObjects) { + try { + return delegate.toArray(query, type, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public T[] toArray(String query, final Class type, final Object... bindObjects) { + try { + return delegate.toArray(query, type, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public List toList(PreparedStatement query, Class componentType, final Object... bindObjects) { + try { + return delegate.toList(query, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public List toList(String query, Class componentType, final Object... bindObjects) { + try { + return delegate.toList(query, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public Map toMap(PreparedStatement query, Class mapKeyType, Class componentType, final Object... bindObjects) { + try { + return delegate.toMap(query, mapKeyType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public Map toMap(String query, Class mapKeyType, Class componentType, final Object... bindObjects) { + try { + return delegate.toMap(query, mapKeyType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , C> Map toMapList(PreparedStatement query, Class mapKeyType, Class componentType, final Object... bindObjects) { + try { + return delegate.toMapList(query, mapKeyType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , C> Map toMapList(String query, Class mapKeyType, Class componentType, final Object... bindObjects) { + try { + return delegate.toMapList(query, mapKeyType, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , E extends Map, V> T toCollectionMap(PreparedStatement query, final Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toCollectionMap(query, collectionType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , E extends Map, V> T toCollectionMap(String query, final Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toCollectionMap(query, collectionType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , E extends Map, V> T toCollectionMap(PreparedStatement query, T list, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toCollectionMap(query, list, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , E extends Map, V> T toCollectionMap(String query, T list, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toCollectionMap(query, list, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, E extends Map, V> T toMapMap(PreparedStatement query, final Class returnType, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapMap(query, returnType, mapKeyType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, E extends Map, V> T toMapMap(String query, final Class returnType, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapMap(query, returnType, mapKeyType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, E extends Map, V> T toMapMap(PreparedStatement query, T map, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapMap(query, map, mapKeyType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, E extends Map, V> T toMapMap(String query, T map, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapMap(query, map, mapKeyType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(PreparedStatement query, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapCollectionMap(query, returnType, mapKeyType, collectionType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(String query, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapCollectionMap(query, returnType, mapKeyType, collectionType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(PreparedStatement query, T map, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapCollectionMap(query, map, mapKeyType, collectionType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(String query, T map, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapCollectionMap(query, map, mapKeyType, collectionType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> ListIterator> toListIteratorMap(PreparedStatement query, final Class type, Class mapValType, final Object... bindObjects) { + try { + return delegate.toListIteratorMap(query, type, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> ListIterator> toListIteratorMap(String query, final Class type, Class mapValType, final Object... bindObjects) { + try { + return delegate.toListIteratorMap(query, type, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> Iterator> toIteratorMap(PreparedStatement query, final Class type, Class mapValType, final Object... bindObjects) { + try { + return delegate.toIteratorMap(query, type, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> Iterator> toIteratorMap(String query, final Class type, Class mapValType, final Object... bindObjects) { + try { + return delegate.toIteratorMap(query, type, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> Map[] toArrayMap(PreparedStatement query, final Class type, Class mapValType, final Object... bindObjects) { + try { + return delegate.toArrayMap(query, type, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> Map[] toArrayMap(String query, final Class type, Class mapValType, final Object... bindObjects) { + try { + return delegate.toArrayMap(query, type, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> List> toListMap(PreparedStatement query, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toListMap(query, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> List> toListMap(String query, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toListMap(query, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> Map> toMapMap(PreparedStatement query, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapMap(query, mapKeyType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> Map> toMapMap(String query, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapMap(query, mapKeyType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> Map>> toMapListMap(PreparedStatement query, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapListMap(query, mapKeyType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> Map>> toMapListMap(String query, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapListMap(query, mapKeyType, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public ListIterator> toListIteratorMap(PreparedStatement query, Class mapValType, final Object... bindObjects) { + try { + return delegate.toListIteratorMap(query, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public ListIterator> toListIteratorMap(String query, Class mapValType, final Object... bindObjects) { + try { + return delegate.toListIteratorMap(query, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public Iterator> toIteratorMap(PreparedStatement query, Class mapValType, final Object... bindObjects) { + try { + return delegate.toIteratorMap(query, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public Iterator> toIteratorMap(String query, Class mapValType, final Object... bindObjects) { + try { + return delegate.toIteratorMap(query, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public List> toListMap(PreparedStatement query, Class mapValType, final Object... bindObjects) { + try { + return delegate.toListMap(query, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public List> toListMap(String query, Class mapValType, final Object... bindObjects) { + try { + return delegate.toListMap(query, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public Map> toMapMap(PreparedStatement query, Class mapKeyType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapMap(query, mapKeyType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public Map> toMapMap(String query, Class mapKeyType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapMap(query, mapKeyType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public Map>> toMapListMap(PreparedStatement query, Class mapKeyType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapListMap(query, mapKeyType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public Map>> toMapListMap(String query, Class mapKeyType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toMapListMap(query, mapKeyType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + +} + diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/QueryMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/QueryMapper.java new file mode 100644 index 0000000..80953ea --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/QueryMapper.java @@ -0,0 +1,780 @@ +package com.moparisthebest.jdbc; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.sql.DataSource; +import java.io.*; +import java.sql.*; +import java.util.*; + +import static com.moparisthebest.jdbc.TryClose.tryClose; + +public class QueryMapper implements Closeable { + + protected final ResultSetMapper cm; + protected final Connection conn; + protected final Context context; + + protected QueryMapper(Connection conn, String jndiName, ResultSetMapper cm) { + this.cm = cm == null ? new ResultSetMapper() : cm; + Context context = null; + if (conn == null && jndiName != null) + try { + //todo: EnsureContext.setup(); + context = new InitialContext(); + DataSource ds = (DataSource) context.lookup(jndiName); + conn = ds.getConnection(); + } catch (Throwable e) { + e.printStackTrace(); + tryClose(conn); + tryClose(context); + } + this.conn = conn; + this.context = context; + if (this.conn == null) + throw new NullPointerException("Connection needs to be non-null for QueryMapper..."); + } + + public QueryMapper(Connection conn, ResultSetMapper cm) { + this(conn, null, cm); + } + + public QueryMapper(Connection conn) { + this(conn, null); + } + + public QueryMapper(String jndiName, ResultSetMapper cm) { + this(null, jndiName, cm); + } + + public QueryMapper(String jndiName) { + this(jndiName, null); + } + + /** + * Only meant to be called by implementing classes + */ + protected QueryMapper() { + this.cm = null; + this.conn = null; + this.context = null; + } + + @Override + public void close() { + if (context != null) { + tryClose(conn); + tryClose(context); + } + } + + private static class StringWrapper { + public final String s; + + private StringWrapper(String s) { + this.s = s; + } + + public String toString() { + return s; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StringWrapper)) return false; + StringWrapper that = (StringWrapper) o; + return !(s != null ? !s.equals(that.s) : that.s != null); + } + + public int hashCode() { + return s != null ? s.hashCode() : 0; + } + } + + private static class ClobString extends StringWrapper { + private ClobString(String s) { + super(s); + } + } + + private static class BlobString extends StringWrapper { + private BlobString(String s) { + super(s); + } + } + + public static Object wrapClob(String s) { + return new ClobString(s); + } + + public static Object wrapBlob(String s) { + return new BlobString(s); + } + + public static void setObject(final PreparedStatement ps, final int index, final Object o) throws SQLException { + // we are going to put most common ones up top so it should execute faster normally + if (o == null || o instanceof String || o instanceof Number) + ps.setObject(index, o); + // java.util.Date support, put it in a Timestamp + else if (o instanceof java.util.Date) + ps.setObject(index, o.getClass().equals(java.util.Date.class) ? new java.sql.Timestamp(((java.util.Date)o).getTime()) : o); + // CLOB support + else if (o instanceof Reader) + ps.setClob(index, (Reader) o); + else if (o instanceof ClobString) + ps.setObject(index, ((ClobString) o).s == null ? null : ((ClobString) o).s); + else if (o instanceof Clob) + ps.setClob(index, (Clob) o); + // BLOB support + else if (o instanceof byte[]) + ps.setBlob(index, new ByteArrayInputStream((byte[]) o)); + else if (o instanceof InputStream) + ps.setBlob(index, (InputStream) o); + else if (o instanceof File) + try { + ps.setBlob(index, new FileInputStream((File) o)); + } catch (FileNotFoundException e) { + throw new SQLException("File to Blob FileNotFoundException", e); + } + else if (o instanceof BlobString) + try { + ps.setBlob(index, ((BlobString) o).s == null ? null : new ByteArrayInputStream(((BlobString) o).s.getBytes("UTF-8"))); + } catch (UnsupportedEncodingException e) { + throw new SQLException("String to Blob UnsupportedEncodingException", e); + } + else if (o instanceof Blob) + ps.setBlob(index, (Blob) o); + else + ps.setObject(index, o); // probably won't get here ever, but just in case... + /* + switch(ps.getParameterMetaData().getParameterType(index)){ // 'java.sql.SQLException: Unsupported feature', fully JDBC 3.0 compliant my ass, freaking oracle... + case Types.CLOB: + if(o instanceof String) + ps.setObject(index, o); + else if (o instanceof Reader) + ps.setClob(index, (Reader) o); + else if (o instanceof Clob) + ps.setClob(index, (Clob) o); + return; + case Types.BLOB: + if (o instanceof byte[]) + ps.setBlob(index, new ByteArrayInputStream((byte[])o)); + else if (o instanceof InputStream) + ps.setBlob(index, (InputStream) o); + else if (o instanceof File) + try { + ps.setBlob(index, new FileInputStream((File) o)); + } catch (FileNotFoundException e) { + throw new SQLException("File to Blob FileNotFoundException", e); + } + else if (o instanceof Blob) + ps.setBlob(index, (Blob) o); + else if(o instanceof String) + try{ + ps.setBlob(index, new ByteArrayInputStream(((String) o).getBytes("UTF-8"))); + }catch(UnsupportedEncodingException e){ + throw new SQLException("String to Blob UnsupportedEncodingException", e); + } + return; + default: + ps.setObject(index, o); + } + */ + } + + public static int recursiveBind(final PreparedStatement ps, final Object... bindObjects) throws SQLException { + return recursiveBind(ps, 0, bindObjects); + } + + private static int recursiveBind(final PreparedStatement ps, int index, final Object... bindObjects) throws SQLException { + if (bindObjects != null && bindObjects.length > 0) { + for (Object o : bindObjects) { + if (o != null) { + if (o instanceof Object[]) { + index = recursiveBind(ps, index, (Object[]) o); + continue; + } else if (o instanceof Collection) { + index = recursiveBind(ps, index, ((Collection) o).toArray()); + continue; + } + } + //System.out.printf("index: '%d' bound to '%s'\n", index+1, o); + setObject(ps, ++index, o); + //ps.setObject(++index, o); + } + } + return index; + } + + public static PreparedStatement bindStatement(final PreparedStatement ps, final Object... bindObjects) throws SQLException { + recursiveBind(ps, bindObjects); + return ps; + } + + protected PreparedStatement bind(final PreparedStatement ps, final Object... bindObjects) throws SQLException { + return bindStatement(ps, bindObjects); + } + + protected ResultSet bindExecute(final PreparedStatement ps, final Object... bindObjects) throws SQLException { + return bind(ps, bindObjects).executeQuery(); + } + + // these update the database + + public int executeUpdate(PreparedStatement ps, final Object... bindObjects) throws SQLException { + return bind(ps, bindObjects).executeUpdate(); + } + + public boolean executeUpdateSuccess(PreparedStatement ps, final Object... bindObjects) throws SQLException { + return this.executeUpdate(ps, bindObjects) >= 0; + } + + public int executeUpdate(String sql, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.executeUpdate(ps, bindObjects); + } finally { + tryClose(ps); + } + } + + public boolean executeUpdateSuccess(String sql, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.executeUpdateSuccess(ps, bindObjects); + } finally { + tryClose(ps); + } + } + + // these update the database using UpdateableDTOs + + public int updateRows(UpdateableDTO dto) throws SQLException { + return dto.updateRow(this); + } + + public int updateRows(Collection dtos) throws SQLException { + int count = 0; + if (dtos != null) + for (UpdateableDTO dto : dtos) + count += dto.updateRow(this); + return count; + } + + public int updateRows(UpdateableDTO[] dtos) throws SQLException { + return updateRows(Arrays.asList(dtos)); + } + + public int insertRows(UpdateableDTO dto) throws SQLException { + return dto.insertRow(this); + } + + public int insertRows(Collection dtos) throws SQLException { + int count = 0; + if (dtos != null) + for (UpdateableDTO dto : dtos) + count += dto.insertRow(this); + return count; + } + + public int insertRows(UpdateableDTO[] dtos) throws SQLException { + return insertRows(Arrays.asList(dtos)); + } + + // these grab ResultSets from the database + + public ResultSet toResultSet(PreparedStatement ps, final Object... bindObjects) throws SQLException { + return toResultSet(ps, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, bindObjects); + } + + public ResultSet toResultSet(PreparedStatement ps, int rsType, int rsConcurrency, final Object... bindObjects) throws SQLException { + return bindExecute(ps, bindObjects); + } + + public ResultSet toResultSet(String sql, final Object... bindObjects) throws SQLException { + return toResultSet(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, bindObjects); + } + + public ResultSet toResultSet(String sql, Integer rsType, Integer rsConcurrency, final Object... bindObjects) throws SQLException { + //throw new UnsupportedOperationException("Can't return ResultSet from String because the PreparedStatement can't be closed before the ResultSet is, so CachingQueryMapper will work."); + // works with StatementClosingResultSet + boolean error = true; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = conn.prepareStatement(sql, rsType.intValue(), rsConcurrency.intValue()); + rs = this.toResultSet(ps, bindObjects); + error = false; + return new StatementClosingResultSet(rs, ps); + } finally { + if (error) { + tryClose(rs); + tryClose(ps); + } + } + } + + // these are standard getters + + public ResultSetMapper getCustomResultSetMapper() { + return cm; + } + + public Connection getConnection() { + return conn; + } + + // DO NOT EDIT BELOW THIS LINE, OR CHANGE THIS COMMENT, CODE AUTOMATICALLY GENERATED BY genQueryMapper.sh + + public T toObject(PreparedStatement ps, Class componentType, final Object... bindObjects) throws SQLException { + return cm.toObject(bindExecute(ps, bindObjects), componentType); + } + + public T toObject(String sql, Class componentType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toObject(ps, componentType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , V> Map toSingleMap(PreparedStatement ps, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toSingleMap(bindExecute(ps, bindObjects), componentType, mapValType); + } + + public , V> Map toSingleMap(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toSingleMap(ps, componentType, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public Map toSingleMap(PreparedStatement ps, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toSingleMap(bindExecute(ps, bindObjects), mapValType); + } + + public Map toSingleMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toSingleMap(ps, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , E> T toCollection(PreparedStatement ps, final Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { + return cm.toCollection(bindExecute(ps, bindObjects), collectionType, componentType); + } + + public , E> T toCollection(String sql, final Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toCollection(ps, collectionType, componentType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , E> T toCollection(PreparedStatement ps, T list, Class componentType, final Object... bindObjects) throws SQLException { + return cm.toCollection(bindExecute(ps, bindObjects), list, componentType); + } + + public , E> T toCollection(String sql, T list, Class componentType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toCollection(ps, list, componentType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , K, E> T toMap(PreparedStatement ps, final Class returnType, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + return cm.toMap(bindExecute(ps, bindObjects), returnType, mapKeyType, componentType); + } + + public , K, E> T toMap(String sql, final Class returnType, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMap(ps, returnType, mapKeyType, componentType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , K, E> T toMap(PreparedStatement ps, T map, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + return cm.toMap(bindExecute(ps, bindObjects), map, mapKeyType, componentType); + } + + public , K, E> T toMap(String sql, T map, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMap(ps, map, mapKeyType, componentType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , K, E extends Collection, C> T toMapCollection(PreparedStatement ps, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { + return cm.toMapCollection(bindExecute(ps, bindObjects), returnType, mapKeyType, collectionType, componentType); + } + + public , K, E extends Collection, C> T toMapCollection(String sql, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMapCollection(ps, returnType, mapKeyType, collectionType, componentType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , K, E extends Collection, C> T toMapCollection(PreparedStatement ps, T map, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { + return cm.toMapCollection(bindExecute(ps, bindObjects), map, mapKeyType, collectionType, componentType); + } + + public , K, E extends Collection, C> T toMapCollection(String sql, T map, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMapCollection(ps, map, mapKeyType, collectionType, componentType, bindObjects); + } finally { + tryClose(ps); + } + } + + public ListIterator toListIterator(PreparedStatement ps, final Class type, final Object... bindObjects) throws SQLException { + return cm.toListIterator(bindExecute(ps, bindObjects), type); + } + + public ListIterator toListIterator(String sql, final Class type, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toListIterator(ps, type, bindObjects); + } finally { + tryClose(ps); + } + } + + public Iterator toIterator(PreparedStatement ps, final Class type, final Object... bindObjects) throws SQLException { + return cm.toIterator(bindExecute(ps, bindObjects), type); + } + + public Iterator toIterator(String sql, final Class type, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toIterator(ps, type, bindObjects); + } finally { + tryClose(ps); + } + } + + public T[] toArray(PreparedStatement ps, final Class type, final Object... bindObjects) throws SQLException { + return cm.toArray(bindExecute(ps, bindObjects), type); + } + + public T[] toArray(String sql, final Class type, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toArray(ps, type, bindObjects); + } finally { + tryClose(ps); + } + } + + public List toList(PreparedStatement ps, Class componentType, final Object... bindObjects) throws SQLException { + return cm.toList(bindExecute(ps, bindObjects), componentType); + } + + public List toList(String sql, Class componentType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toList(ps, componentType, bindObjects); + } finally { + tryClose(ps); + } + } + + public Map toMap(PreparedStatement ps, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + return cm.toMap(bindExecute(ps, bindObjects), mapKeyType, componentType); + } + + public Map toMap(String sql, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMap(ps, mapKeyType, componentType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , C> Map toMapList(PreparedStatement ps, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + return cm.toMapList(bindExecute(ps, bindObjects), mapKeyType, componentType); + } + + public , C> Map toMapList(String sql, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMapList(ps, mapKeyType, componentType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , E extends Map, V> T toCollectionMap(PreparedStatement ps, final Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toCollectionMap(bindExecute(ps, bindObjects), collectionType, componentType, mapValType); + } + + public , E extends Map, V> T toCollectionMap(String sql, final Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toCollectionMap(ps, collectionType, componentType, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , E extends Map, V> T toCollectionMap(PreparedStatement ps, T list, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toCollectionMap(bindExecute(ps, bindObjects), list, componentType, mapValType); + } + + public , E extends Map, V> T toCollectionMap(String sql, T list, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toCollectionMap(ps, list, componentType, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , K, E extends Map, V> T toMapMap(PreparedStatement ps, final Class returnType, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toMapMap(bindExecute(ps, bindObjects), returnType, mapKeyType, componentType, mapValType); + } + + public , K, E extends Map, V> T toMapMap(String sql, final Class returnType, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMapMap(ps, returnType, mapKeyType, componentType, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , K, E extends Map, V> T toMapMap(PreparedStatement ps, T map, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toMapMap(bindExecute(ps, bindObjects), map, mapKeyType, componentType, mapValType); + } + + public , K, E extends Map, V> T toMapMap(String sql, T map, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMapMap(ps, map, mapKeyType, componentType, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(PreparedStatement ps, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toMapCollectionMap(bindExecute(ps, bindObjects), returnType, mapKeyType, collectionType, componentType, mapValType); + } + + 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 { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMapCollectionMap(ps, returnType, mapKeyType, collectionType, componentType, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(PreparedStatement ps, T map, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toMapCollectionMap(bindExecute(ps, bindObjects), map, mapKeyType, collectionType, componentType, mapValType); + } + + 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 { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMapCollectionMap(ps, map, mapKeyType, collectionType, componentType, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , V> ListIterator> toListIteratorMap(PreparedStatement ps, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toListIteratorMap(bindExecute(ps, bindObjects), type, mapValType); + } + + public , V> ListIterator> toListIteratorMap(String sql, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toListIteratorMap(ps, type, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , V> Iterator> toIteratorMap(PreparedStatement ps, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toIteratorMap(bindExecute(ps, bindObjects), type, mapValType); + } + + public , V> Iterator> toIteratorMap(String sql, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toIteratorMap(ps, type, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , V> Map[] toArrayMap(PreparedStatement ps, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toArrayMap(bindExecute(ps, bindObjects), type, mapValType); + } + + public , V> Map[] toArrayMap(String sql, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toArrayMap(ps, type, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , V> List> toListMap(PreparedStatement ps, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toListMap(bindExecute(ps, bindObjects), componentType, mapValType); + } + + public , V> List> toListMap(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toListMap(ps, componentType, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , V> Map> toMapMap(PreparedStatement ps, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toMapMap(bindExecute(ps, bindObjects), mapKeyType, componentType, mapValType); + } + + public , V> Map> toMapMap(String sql, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMapMap(ps, mapKeyType, componentType, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public , V> Map>> toMapListMap(PreparedStatement ps, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toMapListMap(bindExecute(ps, bindObjects), mapKeyType, componentType, mapValType); + } + + public , V> Map>> toMapListMap(String sql, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMapListMap(ps, mapKeyType, componentType, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public ListIterator> toListIteratorMap(PreparedStatement ps, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toListIteratorMap(bindExecute(ps, bindObjects), mapValType); + } + + public ListIterator> toListIteratorMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toListIteratorMap(ps, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public Iterator> toIteratorMap(PreparedStatement ps, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toIteratorMap(bindExecute(ps, bindObjects), mapValType); + } + + public Iterator> toIteratorMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toIteratorMap(ps, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public List> toListMap(PreparedStatement ps, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toListMap(bindExecute(ps, bindObjects), mapValType); + } + + public List> toListMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toListMap(ps, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public Map> toMapMap(PreparedStatement ps, Class mapKeyType, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toMapMap(bindExecute(ps, bindObjects), mapKeyType, mapValType); + } + + public Map> toMapMap(String sql, Class mapKeyType, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMapMap(ps, mapKeyType, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + + public Map>> toMapListMap(PreparedStatement ps, Class mapKeyType, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toMapListMap(bindExecute(ps, bindObjects), mapKeyType, mapValType); + } + + public Map>> toMapListMap(String sql, Class mapKeyType, Class mapValType, final Object... bindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sql); + return this.toMapListMap(ps, mapKeyType, mapValType, bindObjects); + } finally { + tryClose(ps); + } + } + +} + diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java new file mode 100644 index 0000000..4e0b8ea --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java @@ -0,0 +1,973 @@ +package com.moparisthebest.jdbc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Header:$ + */ + +import java.lang.ref.SoftReference; +import java.lang.reflect.*; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.*; +import java.util.concurrent.*; + +/** + * Default ResultSetMapper implementation for Objects. + *

+ * This class is heavily modified from org.apache.beehive.controls.system.jdbc.DefaultObjectResultSetMapper + *

+ * This uses CustomRowToObjectMapper to perform the actual mapping to individual classes, so look to that + * implementation for details. + *

+ * This supports mapping to a single object, or collections of Objects, currently it supports: + * 1. arrays + * 2. anything implementing java.util.Collection with a public no-arg constructor + * 3. anything implementing java.util.Map with a public no-arg constructor + * a. The first column in the ResultSet will be the Map's key + * b. If there are only two columns, the second will be the Map's value + * c. If there are more than two columns, the value will be mapped to an object with the entire ResultSet in it, + * including the key, just like returning a Collection would do + * 4. Iterators returned by a Collection, or ListIterators returned by a List + *

+ * It has sensible default implementations to return if you specify an interface, look in Map 'interfaceToConcrete' + * for the default values returned for specific interfaces. If not specified in there, Map's default is HashMap, + * while Collection's default is ArrayList. + *

+ * Anything else must be a concrete class with a public no-arg constructor to instantiate it + * + * @author Travis Burtrum (modifications from beehive) + */ +public class ResultSetMapper { + + public static final Map interfaceToConcrete = Collections.unmodifiableMap(new HashMap() {{ + // Collection's + put(Collection.class, ArrayList.class); + // List + put(List.class, ArrayList.class); + // Set's + put(Set.class, HashSet.class); + // SortedSet's + put(SortedSet.class, TreeSet.class); + put(NavigableSet.class, ConcurrentSkipListSet.class); + // Queue's + put(Queue.class, LinkedList.class); + put(Deque.class, LinkedList.class); + // BlockingQueue's + put(BlockingQueue.class, LinkedBlockingQueue.class); + put(BlockingDeque.class, LinkedBlockingDeque.class); + + // Map's + put(Map.class, HashMap.class); + // ConcurrentMap's + put(ConcurrentMap.class, ConcurrentHashMap.class); + put(ConcurrentNavigableMap.class, ConcurrentSkipListMap.class); + // SortedMap's + put(SortedMap.class, TreeMap.class); + put(NavigableMap.class, TreeMap.class); + }}); + + @SuppressWarnings({"unchecked"}) + public static Class getConcreteClass(final Class returnType, final Class defaultConcreteClass) { + int classModifiers = returnType.getModifiers(); + if (Modifier.isInterface(classModifiers) || Modifier.isAbstract(classModifiers)) { + Class concrete = (Class) interfaceToConcrete.get(returnType); + return concrete == null ? (Class) defaultConcreteClass : concrete; + } + return returnType; + } + + public static T instantiateClass(final Class returnType, final Class defaultConcreteClass) { + try { + return getConcreteClass(returnType, defaultConcreteClass).newInstance(); + } catch (Throwable e) { + throw new MapperException("Failed to instantiate class of type " + returnType.getName(), e); + } + } + + private final Calendar cal; + private final int arrayMaxLength; + + public ResultSetMapper(Calendar cal, int arrayMaxLength) { + this.cal = cal; + this.arrayMaxLength = arrayMaxLength; + } + + public ResultSetMapper() { + this(1024); + } + + public ResultSetMapper(int arrayMaxLength) { + this(null, arrayMaxLength); + } + + protected T privToObject(ResultSet rs, Class componentType, Calendar cal, Class mapValType) { + T ret = null; + try { + if (rs.next()) + ret = getRowMapper(rs, componentType, cal, mapValType).mapRowToReturnType(); + } catch (SQLException e) { + // ignore + } + tryClose(rs); + return ret == null ? RowToObjectMapper.fixNull(componentType) : ret; + } + + protected , E> T privToCollection(ResultSet rs, final Class collectionType, Class componentType, int arrayMaxLength, Calendar cal, Class mapValType) { + return privToCollection(rs, instantiateClass(collectionType, ArrayList.class), componentType, arrayMaxLength, cal, mapValType); + } + + /** + * Invoked when the return type of the method is an array type. + * + * @param rs ResultSet to process. + * @param componentType The class of object contained within the array + * @param arrayMaxLength The maximum size of array to create, a value of 0 indicates that the array + * size will be the same as the result set size (no limit). + * @param cal A calendar instance to use for date/time values + * @return An array of the specified class type + */ + protected , E> T privToCollection(ResultSet rs, T list, Class componentType, int arrayMaxLength, Calendar cal, Class mapValType) { + int numRows = 0; + // dereference list in an attempt to gracefully avoid OutOfMemoryError's + final SoftReference softList = new SoftReference(list); + list = null; + try { + // a value of less than 1 indicates that all rows from the ResultSet should be included. + final boolean unlimitedRows = arrayMaxLength < 1; + + final RowToObjectMapper rowMapper = getRowMapper(rs, componentType, cal, mapValType); + + for (; (unlimitedRows || numRows != arrayMaxLength) && rs.next(); ++numRows) { + E object = rowMapper.mapRowToReturnType(); + + list = softList.get(); + if (list == null) + throw new OutOfMemoryError(); + list.add(object); + list = null; + } + + if (!unlimitedRows) + warnOnMaxLength(numRows, arrayMaxLength, rs); + + list = softList.get(); + if (list == null) + throw new OutOfMemoryError(); + + // if this list is Finishable, call finish() + if (list instanceof Finishable) + ((Finishable) list).finish(rs); + + tryClose(rs); + return list; + } catch(OutOfMemoryError e){ + tryClose(rs); + throw new MapperException(String.format("Too many rows (processed %d, max %d), ran out of memory.", numRows, arrayMaxLength), e); + } catch(Throwable e) { + if(list == null) + list = softList.get(); + String columnName = "UNKNOWN"; + String columnType = "UNKNOWN"; + String returnedType = "UNKNOWN"; + int badColumn = 0; + try { + ResultSetMetaData md = rs.getMetaData(); + badColumn = e.getCause() instanceof SQLExceptionColumnNum ? ((SQLExceptionColumnNum)e.getCause()).getColumnNum() : 1; + columnName = md.getColumnName(badColumn); + columnType = md.getColumnTypeName(badColumn); + returnedType = ((list != null && !list.isEmpty()) ? list.iterator().next() : rs.getObject(badColumn)).getClass().getName(); + } catch (Throwable t) { + // ignore + } + tryClose(rs); + // assuming no errors in resultSetObject() this can only happen + // for single column result sets. + throw new MapperException("The declared Java type for " + + String.format("%s<%s>" + , (list != null ? list.getClass() : "UnknownCollection") + , getComponentName(componentType, mapValType)) + + " is incompatible with the SQL format of column " + badColumn + " '" + columnName + + "' (" + columnType + ") which returns objects of type " + returnedType, + e); + } + } + + protected , K, E> T privToMap(ResultSet rs, final Class returnType, Class mapKeyType, Class componentType, int arrayMaxLength, Calendar cal, Class mapValType) { + return privToMap(rs, instantiateClass(returnType, HashMap.class), mapKeyType, componentType, arrayMaxLength, cal, mapValType); + } + + /** + * Invoked when the return type of the method is a Map type. + * + * @param rs ResultSet to process. + * @param arrayMaxLength The maximum size of array to create, a value of 0 indicates that the array + * size will be the same as the result set size (no limit). + * @param cal A calendar instance to use for date/time values + * @return An array of the specified class type + */ + protected , K, E> T privToMap(ResultSet rs, T map, Class mapKeyType, Class componentType, int arrayMaxLength, Calendar cal, Class mapValType) { + int numRows = 0; + // dereference map in an attempt to gracefully avoid OutOfMemoryError's + final SoftReference softMap = new SoftReference(map); + map = null; + try { + // a value of less than 1 indicates that all rows from the ResultSet should be included. + final boolean unlimitedRows = arrayMaxLength < 1; + + final RowToObjectMapper rowMapper = getRowMapper(rs, componentType, cal, mapValType); + + boolean onlyTwoColumns = rowMapper.getColumnCount() == 2; + for (; (unlimitedRows || numRows != arrayMaxLength) && rs.next(); ++numRows) { + K key = rowMapper.extractColumnValue(1, mapKeyType); + E value = onlyTwoColumns ? rowMapper.extractColumnValue(2, componentType) : rowMapper.mapRowToReturnType(); + + map = softMap.get(); + if (map == null) + throw new OutOfMemoryError(); + map.put(key, value); + map = null; + } + + if (!unlimitedRows) + warnOnMaxLength(numRows, arrayMaxLength, rs); + + map = softMap.get(); + if (map == null) + throw new OutOfMemoryError(); + + // if this map is Finishable, call finish() + if (map instanceof Finishable) + ((Finishable) map).finish(rs); + + tryClose(rs); + return map; + } catch(OutOfMemoryError e){ + tryClose(rs); + throw new MapperException(String.format("Too many rows (processed %d, max %d), ran out of memory.", numRows, arrayMaxLength), e); + }catch (Throwable e) { + if(map == null) + map = softMap.get(); + String columnName = "UNKNOWN"; + String columnType = "UNKNOWN"; + String returnedType = "UNKNOWN"; + int badColumn = 0; + try { + ResultSetMetaData md = rs.getMetaData(); + badColumn = e.getCause() instanceof SQLExceptionColumnNum ? ((SQLExceptionColumnNum)e.getCause()).getColumnNum() : 1; + columnName = md.getColumnName(badColumn); + columnType = md.getColumnTypeName(badColumn); + returnedType = ((map != null && !map.isEmpty()) ? map.values().iterator().next() : rs.getObject(badColumn)).getClass().getName(); + } catch (Throwable t) { + // ignore + } + tryClose(rs); + // assuming no errors in resultSetObject() this can only happen + // for single column result sets. + throw new MapperException("The declared Java type for " + + String.format("%s<%s, %s>" + , (map != null ? map.getClass() : "UnknownMap") + , mapKeyType != null ? mapKeyType.getName() : "UNKNOWN" + , getComponentName(componentType, mapValType)) + + " is incompatible with the SQL format of column "+ badColumn + " '" + columnName + + "' (" + columnType + ") which returns objects of type " + returnedType, + e); + } + } + + protected , K, E extends Collection, C> T privToMapCollection(ResultSet rs, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, int arrayMaxLength, Calendar cal, Class mapValType) { + return privToMapCollection(rs, instantiateClass(returnType, HashMap.class), mapKeyType, collectionType, componentType, arrayMaxLength, cal, mapValType); + } + + /** + * Invoked when the return type of the method is a Map type with a list for a value. + * + * @param rs ResultSet to process. + * @param arrayMaxLength The maximum size of array to create, a value of 0 indicates that the array + * size will be the same as the result set size (no limit). + * @param cal A calendar instance to use for date/time values + * @return An array of the specified class type + */ + protected , K, E extends Collection, C> T privToMapCollection(ResultSet rs, T map, Class mapKeyType, Class collectionType, Class componentType, int arrayMaxLength, Calendar cal, Class mapValType) { + int numRows = 0; + // dereference map in an attempt to gracefully avoid OutOfMemoryError's + final SoftReference softMap = new SoftReference(map); + map = null; + try { + // a value of less than 1 indicates that all rows from the ResultSet should be included. + final boolean unlimitedRows = arrayMaxLength < 1; + + final RowToObjectMapper rowMapper = getRowMapper(rs, componentType, cal, mapValType); + + boolean onlyTwoColumns = rowMapper.getColumnCount() == 2; + for (; (unlimitedRows || numRows != arrayMaxLength) && rs.next(); ++numRows) { + K key = rowMapper.extractColumnValue(1, mapKeyType); + C value = onlyTwoColumns ? rowMapper.extractColumnValue(2, componentType) : rowMapper.mapRowToReturnType(); + + map = softMap.get(); + if (map == null) + throw new OutOfMemoryError(); + E list = map.get(key); + if(list == null){ + list = instantiateClass(collectionType, ArrayList.class); + map.put(key, list); + } + list.add(value); + map = null; + } + + if (!unlimitedRows) + warnOnMaxLength(numRows, arrayMaxLength, rs); + + map = softMap.get(); + if (map == null) + throw new OutOfMemoryError(); + + // if this map is Finishable, call finish() + if (map instanceof Finishable) + ((Finishable) map).finish(rs); + + tryClose(rs); + return map; + } catch(OutOfMemoryError e){ + tryClose(rs); + throw new MapperException(String.format("Too many rows (processed %d, max %d), ran out of memory.", numRows, arrayMaxLength), e); + }catch (Throwable e) { + if(map == null) + map = softMap.get(); + String columnName = "UNKNOWN"; + String columnType = "UNKNOWN"; + String returnedType = "UNKNOWN"; + int badColumn = 0; + try { + ResultSetMetaData md = rs.getMetaData(); + badColumn = e.getCause() instanceof SQLExceptionColumnNum ? ((SQLExceptionColumnNum)e.getCause()).getColumnNum() : 1; + columnName = md.getColumnName(badColumn); + columnType = md.getColumnTypeName(badColumn); + returnedType = ((map != null && !map.isEmpty()) ? map.values().iterator().next() : rs.getObject(badColumn)).getClass().getName(); + } catch (Throwable t) { + // ignore + } + tryClose(rs); + // assuming no errors in resultSetObject() this can only happen + // for single column result sets. + throw new MapperException("The declared Java type for " + + String.format("%s<%s, %s>" + , (map != null ? map.getClass() : "UnknownMap") + , mapKeyType != null ? mapKeyType.getName() : "UNKNOWN" + , getComponentName(componentType, mapValType)) + + " is incompatible with the SQL format of column "+ badColumn + " '" + columnName + + "' (" + columnType + ") which returns objects of type " + returnedType, + e); + } + } + + + // methods using toMapCollection to return different types + protected ListIterator privToListIterator(ResultSet rs, final Class type, int arrayMaxLength, Calendar cal, Class mapValType) { + return privToList(rs, type, arrayMaxLength, cal, mapValType).listIterator(); + } + + protected Iterator privToIterator(ResultSet rs, final Class type, int arrayMaxLength, Calendar cal, Class mapValType) { + return privToList(rs, type, arrayMaxLength, cal, mapValType).iterator(); + } + + @SuppressWarnings({"unchecked"}) + protected T[] privToArray(ResultSet rs, final Class type, int arrayMaxLength, Calendar cal, Class mapValType) { + List list = privToList(rs, type, arrayMaxLength, cal, mapValType); + return list.toArray((T[]) Array.newInstance(type, list.size())); + } + + protected List privToList(ResultSet rs, Class componentType, int arrayMaxLength, Calendar cal, Class mapValType) { + return privToCollection(rs, new ArrayList(), componentType, arrayMaxLength, cal, mapValType); + } + + protected Map privToMap(ResultSet rs, Class mapKeyType, Class componentType, int arrayMaxLength, Calendar cal, Class mapValType) { + return privToMap(rs, new HashMap(), mapKeyType, componentType, arrayMaxLength, cal, mapValType); + } + + protected , C> Map privToMapCollection(ResultSet rs, Class mapKeyType, Class collectionType, Class componentType, int arrayMaxLength, Calendar cal, Class mapValType) { + return privToMapCollection(rs, new HashMap(), mapKeyType, collectionType, componentType, arrayMaxLength, cal, mapValType); + } + + // fairly un-interesting methods below here + protected RowToObjectMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType) { + return new RowToObjectMapper(resultSet, returnTypeClass, cal, mapValType); + } + + protected void warnOnMaxLength(final int numRows, final int arrayMaxLength, final ResultSet rs) { + if (numRows < arrayMaxLength) + return; + int totalRows = numRows; + try { + if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) { + rs.last(); + totalRows = rs.getRow(); + } else { + totalRows = rs.getRow(); + while (rs.next()) + ++totalRows; + } + if (totalRows == 0) + totalRows = numRows; + } catch (Throwable e) { + e.printStackTrace(); + } + System.err.printf("This JdbcControl query returned %d rows, %s arrayMaxLength (%d), which you most likely never want to happen, investigate!!!!\n", + totalRows, totalRows == numRows ? "equaling" : "exceeding", arrayMaxLength); + Thread.dumpStack(); + } + + // overloaded helper methods + + public T toObject(ResultSet rs, Class componentType, Calendar cal) { + return privToObject(rs, componentType, cal, null); + } + + public , E> T toCollection(ResultSet rs, final Class collectionType, Class componentType, int arrayMaxLength, Calendar cal) { + return privToCollection(rs, collectionType, componentType, arrayMaxLength, cal, null); + } + + public , E> T toCollection(ResultSet rs, T list, Class componentType, int arrayMaxLength, Calendar cal) { + return privToCollection(rs, list, componentType, arrayMaxLength, cal, null); + } + + public , K, E> T toMap(ResultSet rs, final Class returnType, Class mapKeyType, Class componentType, int arrayMaxLength, Calendar cal) { + return privToMap(rs, returnType, mapKeyType, componentType, arrayMaxLength, cal, null); + } + + public , K, E> T toMap(ResultSet rs, T map, Class mapKeyType, Class componentType, int arrayMaxLength, Calendar cal) { + return privToMap(rs, map, mapKeyType, componentType, arrayMaxLength, cal, null); + } + + public , K, E extends Collection, C> T toMapCollection(ResultSet rs, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, int arrayMaxLength, Calendar cal) { + return privToMapCollection(rs, returnType, mapKeyType, collectionType, componentType, arrayMaxLength, cal, null); + } + + public , K, E extends Collection, C> T toMapCollection(ResultSet rs, T map, Class mapKeyType, Class collectionType, Class componentType, int arrayMaxLength, Calendar cal) { + return privToMapCollection(rs, map, mapKeyType, collectionType, componentType, arrayMaxLength, cal, null); + } + + public ListIterator toListIterator(ResultSet rs, final Class type, int arrayMaxLength, Calendar cal) { + return privToListIterator(rs, type, arrayMaxLength, cal, null); + } + + public Iterator toIterator(ResultSet rs, final Class type, int arrayMaxLength, Calendar cal) { + return privToIterator(rs, type, arrayMaxLength, cal, null); + } + + public T[] toArray(ResultSet rs, final Class type, int arrayMaxLength, Calendar cal) { + return privToArray(rs, type, arrayMaxLength, cal, null); + } + + /** + * Returns a simple List of componentType + */ + public List toList(ResultSet rs, Class componentType, int arrayMaxLength, Calendar cal) { + return privToList(rs, componentType, arrayMaxLength, cal, null); + } + + /** + * Returns a simple Map of mapKeyType -> componentType + */ + public Map toMap(ResultSet rs, Class mapKeyType, Class componentType, int arrayMaxLength, Calendar cal) { + return privToMap(rs, mapKeyType, componentType, arrayMaxLength, cal, null); + } + + /** + * Returns a simple Map of mapKeyType -> List + */ + @SuppressWarnings({"unchecked"}) + public , C> Map toMapList(ResultSet rs, Class mapKeyType, Class componentType, int arrayMaxLength, Calendar cal) { + return (Map) privToMapCollection(rs, mapKeyType, List.class, componentType, arrayMaxLength, cal, null); + } + + // map methods + + // the following 6 methods I can find no way to not require an unchecked cast on the object being returned + // please find a way and let me know, until then, I won't provide overloaded methods for them + public , E extends Map, V> T toCollectionMap(ResultSet rs, final Class collectionType, Class componentType, Class mapValType, int arrayMaxLength, Calendar cal) { + return privToCollection(rs, collectionType, componentType, arrayMaxLength, cal, mapValType); + } + + public , E extends Map, V> T toCollectionMap(ResultSet rs, T list, Class componentType, Class mapValType, int arrayMaxLength, Calendar cal) { + return privToCollection(rs, list, componentType, arrayMaxLength, cal, mapValType); + } + + public , K, E extends Map, V> T toMapMap(ResultSet rs, final Class returnType, Class mapKeyType, Class componentType, Class mapValType, int arrayMaxLength, Calendar cal) { + return privToMap(rs, returnType, mapKeyType, componentType, arrayMaxLength, cal, mapValType); + } + + public , K, E extends Map, V> T toMapMap(ResultSet rs, T map, Class mapKeyType, Class componentType, Class mapValType, int arrayMaxLength, Calendar cal) { + return privToMap(rs, map, mapKeyType, componentType, arrayMaxLength, cal, mapValType); + } + + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(ResultSet rs, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, int arrayMaxLength, Calendar cal) { + return privToMapCollection(rs, returnType, mapKeyType, collectionType, componentType, arrayMaxLength, cal, mapValType); + } + + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(ResultSet rs, T map, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, int arrayMaxLength, Calendar cal) { + return privToMapCollection(rs, map, mapKeyType, collectionType, componentType, arrayMaxLength, cal, mapValType); + } + + public , V> Map toSingleMap(ResultSet rs, Class componentType, Class mapValType, Calendar cal) { + return privToObject(rs, componentType, cal, mapValType); + } + + @SuppressWarnings({"unchecked"}) + public , V> ListIterator> toListIteratorMap(ResultSet rs, final Class type, Class mapValType, int arrayMaxLength, Calendar cal) { + return (ListIterator>) privToListIterator(rs, type, arrayMaxLength, cal, mapValType); + } + + @SuppressWarnings({"unchecked"}) + public , V> Iterator> toIteratorMap(ResultSet rs, final Class type, Class mapValType, int arrayMaxLength, Calendar cal) { + return (Iterator>) privToIterator(rs, type, arrayMaxLength, cal, mapValType); + } + + public , V> Map[] toArrayMap(ResultSet rs, final Class type, Class mapValType, int arrayMaxLength, Calendar cal) { + return privToArray(rs, type, arrayMaxLength, cal, mapValType); + } + + @SuppressWarnings({"unchecked"}) + public , V> List> toListMap(ResultSet rs, Class componentType, Class mapValType, int arrayMaxLength, Calendar cal) { + return (List>) privToList(rs, componentType, arrayMaxLength, cal, mapValType); + } + + @SuppressWarnings({"unchecked"}) + public , V> Map> toMapMap(ResultSet rs, Class mapKeyType, Class componentType, Class mapValType, int arrayMaxLength, Calendar cal) { + return (Map>) privToMap(rs, mapKeyType, componentType, arrayMaxLength, cal, mapValType); + } + + @SuppressWarnings({"unchecked"}) + public , V> Map>> toMapListMap(ResultSet rs, Class mapKeyType, Class componentType, Class mapValType, int arrayMaxLength, Calendar cal) { + return (Map>>) privToMapCollection(rs, mapKeyType, List.class, componentType, arrayMaxLength, cal, mapValType); + } + + // overloaded map methods that don't require you to specify the type of map you want + + @SuppressWarnings({"unchecked"}) + public Map toSingleMap(ResultSet rs, Class mapValType, Calendar cal) { + return toSingleMap(rs, Map.class, mapValType, cal); + } + + @SuppressWarnings({"unchecked"}) + public ListIterator> toListIteratorMap(ResultSet rs, Class mapValType, int arrayMaxLength, Calendar cal) { + return toListIteratorMap(rs, Map.class, mapValType, arrayMaxLength, cal); + } + + @SuppressWarnings({"unchecked"}) + public Iterator> toIteratorMap(ResultSet rs, Class mapValType, int arrayMaxLength, Calendar cal) { + return toIteratorMap(rs, Map.class, mapValType, arrayMaxLength, cal); + } + + @SuppressWarnings({"unchecked"}) + public List> toListMap(ResultSet rs, Class mapValType, int arrayMaxLength, Calendar cal) { + return toListMap(rs, Map.class, mapValType, arrayMaxLength, cal); + } + + @SuppressWarnings({"unchecked"}) + public Map> toMapMap(ResultSet rs, Class mapKeyType, Class mapValType, int arrayMaxLength, Calendar cal) { + return toMapMap(rs, mapKeyType, Map.class, mapValType, arrayMaxLength, cal); + } + + @SuppressWarnings({"unchecked"}) + public Map>> toMapListMap(ResultSet rs, Class mapKeyType, Class mapValType, int arrayMaxLength, Calendar cal) { + return this.toMapListMap(rs, mapKeyType, Map.class, mapValType, arrayMaxLength, cal); + } + + // completely useless except inside this class + + private static String getComponentName(Class componentType, Class mapValType){ + final String componentName = componentType != null ? componentType.getName() : "UNKNOWN"; + if(Map.class.isAssignableFrom(componentType)) + return String.format("%s", + componentName, mapValType != null ? mapValType.getName() : "java.lang.Object"); + if(componentType != null && componentType.isArray()) + return componentType.getComponentType().getName()+"[]"; + return componentName; + } + + protected void tryClose(ResultSet rs) { + //todo: if (canCloseResultSet()) + TryClose.tryClose(rs); + } + + // DO NOT EDIT BELOW THIS LINE, OR CHANGE THIS COMMENT, CODE AUTOMATICALLY GENERATED BY genQueryMapper.sh + + public T toObject(ResultSet rs, Class componentType) { + return this.toObject(rs, componentType, cal); + } + + public , V> Map toSingleMap(ResultSet rs, Class componentType, Class mapValType) { + return this.toSingleMap(rs, componentType, mapValType, cal); + } + + public Map toSingleMap(ResultSet rs, Class mapValType) { + return this.toSingleMap(rs, mapValType, cal); + } + + public , E> T toCollection(ResultSet rs, final Class collectionType, Class componentType) { + return this.toCollection(rs, collectionType, componentType, arrayMaxLength, cal); + } + + public , E> T toCollection(ResultSet rs, final Class collectionType, Class componentType, int arrayMaxLength) { + return this.toCollection(rs, collectionType, componentType, arrayMaxLength, cal); + } + + public , E> T toCollection(ResultSet rs, final Class collectionType, Class componentType, Calendar cal) { + return this.toCollection(rs, collectionType, componentType, arrayMaxLength, cal); + } + + public , E> T toCollection(ResultSet rs, T list, Class componentType) { + return this.toCollection(rs, list, componentType, arrayMaxLength, cal); + } + + public , E> T toCollection(ResultSet rs, T list, Class componentType, int arrayMaxLength) { + return this.toCollection(rs, list, componentType, arrayMaxLength, cal); + } + + public , E> T toCollection(ResultSet rs, T list, Class componentType, Calendar cal) { + return this.toCollection(rs, list, componentType, arrayMaxLength, cal); + } + + public , K, E> T toMap(ResultSet rs, final Class returnType, Class mapKeyType, Class componentType) { + return this.toMap(rs, returnType, mapKeyType, componentType, arrayMaxLength, cal); + } + + public , K, E> T toMap(ResultSet rs, final Class returnType, Class mapKeyType, Class componentType, int arrayMaxLength) { + return this.toMap(rs, returnType, mapKeyType, componentType, arrayMaxLength, cal); + } + + public , K, E> T toMap(ResultSet rs, final Class returnType, Class mapKeyType, Class componentType, Calendar cal) { + return this.toMap(rs, returnType, mapKeyType, componentType, arrayMaxLength, cal); + } + + public , K, E> T toMap(ResultSet rs, T map, Class mapKeyType, Class componentType) { + return this.toMap(rs, map, mapKeyType, componentType, arrayMaxLength, cal); + } + + public , K, E> T toMap(ResultSet rs, T map, Class mapKeyType, Class componentType, int arrayMaxLength) { + return this.toMap(rs, map, mapKeyType, componentType, arrayMaxLength, cal); + } + + public , K, E> T toMap(ResultSet rs, T map, Class mapKeyType, Class componentType, Calendar cal) { + return this.toMap(rs, map, mapKeyType, componentType, arrayMaxLength, cal); + } + + public , K, E extends Collection, C> T toMapCollection(ResultSet rs, final Class returnType, Class mapKeyType, Class collectionType, Class componentType) { + return this.toMapCollection(rs, returnType, mapKeyType, collectionType, componentType, arrayMaxLength, cal); + } + + public , K, E extends Collection, C> T toMapCollection(ResultSet rs, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, int arrayMaxLength) { + return this.toMapCollection(rs, returnType, mapKeyType, collectionType, componentType, arrayMaxLength, cal); + } + + public , K, E extends Collection, C> T toMapCollection(ResultSet rs, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, Calendar cal) { + return this.toMapCollection(rs, returnType, mapKeyType, collectionType, componentType, arrayMaxLength, cal); + } + + public , K, E extends Collection, C> T toMapCollection(ResultSet rs, T map, Class mapKeyType, Class collectionType, Class componentType) { + return this.toMapCollection(rs, map, mapKeyType, collectionType, componentType, arrayMaxLength, cal); + } + + public , K, E extends Collection, C> T toMapCollection(ResultSet rs, T map, Class mapKeyType, Class collectionType, Class componentType, int arrayMaxLength) { + return this.toMapCollection(rs, map, mapKeyType, collectionType, componentType, arrayMaxLength, cal); + } + + public , K, E extends Collection, C> T toMapCollection(ResultSet rs, T map, Class mapKeyType, Class collectionType, Class componentType, Calendar cal) { + return this.toMapCollection(rs, map, mapKeyType, collectionType, componentType, arrayMaxLength, cal); + } + + public ListIterator toListIterator(ResultSet rs, final Class type) { + return this.toListIterator(rs, type, arrayMaxLength, cal); + } + + public ListIterator toListIterator(ResultSet rs, final Class type, int arrayMaxLength) { + return this.toListIterator(rs, type, arrayMaxLength, cal); + } + + public ListIterator toListIterator(ResultSet rs, final Class type, Calendar cal) { + return this.toListIterator(rs, type, arrayMaxLength, cal); + } + + public Iterator toIterator(ResultSet rs, final Class type) { + return this.toIterator(rs, type, arrayMaxLength, cal); + } + + public Iterator toIterator(ResultSet rs, final Class type, int arrayMaxLength) { + return this.toIterator(rs, type, arrayMaxLength, cal); + } + + public Iterator toIterator(ResultSet rs, final Class type, Calendar cal) { + return this.toIterator(rs, type, arrayMaxLength, cal); + } + + public T[] toArray(ResultSet rs, final Class type) { + return this.toArray(rs, type, arrayMaxLength, cal); + } + + public T[] toArray(ResultSet rs, final Class type, int arrayMaxLength) { + return this.toArray(rs, type, arrayMaxLength, cal); + } + + public T[] toArray(ResultSet rs, final Class type, Calendar cal) { + return this.toArray(rs, type, arrayMaxLength, cal); + } + + public List toList(ResultSet rs, Class componentType) { + return this.toList(rs, componentType, arrayMaxLength, cal); + } + + public List toList(ResultSet rs, Class componentType, int arrayMaxLength) { + return this.toList(rs, componentType, arrayMaxLength, cal); + } + + public List toList(ResultSet rs, Class componentType, Calendar cal) { + return this.toList(rs, componentType, arrayMaxLength, cal); + } + + public Map toMap(ResultSet rs, Class mapKeyType, Class componentType) { + return this.toMap(rs, mapKeyType, componentType, arrayMaxLength, cal); + } + + public Map toMap(ResultSet rs, Class mapKeyType, Class componentType, int arrayMaxLength) { + return this.toMap(rs, mapKeyType, componentType, arrayMaxLength, cal); + } + + public Map toMap(ResultSet rs, Class mapKeyType, Class componentType, Calendar cal) { + return this.toMap(rs, mapKeyType, componentType, arrayMaxLength, cal); + } + + public , C> Map toMapList(ResultSet rs, Class mapKeyType, Class componentType) { + return this.toMapList(rs, mapKeyType, componentType, arrayMaxLength, cal); + } + + public , C> Map toMapList(ResultSet rs, Class mapKeyType, Class componentType, int arrayMaxLength) { + return this.toMapList(rs, mapKeyType, componentType, arrayMaxLength, cal); + } + + public , C> Map toMapList(ResultSet rs, Class mapKeyType, Class componentType, Calendar cal) { + return this.toMapList(rs, mapKeyType, componentType, arrayMaxLength, cal); + } + + public , E extends Map, V> T toCollectionMap(ResultSet rs, final Class collectionType, Class componentType, Class mapValType) { + return this.toCollectionMap(rs, collectionType, componentType, mapValType, arrayMaxLength, cal); + } + + public , E extends Map, V> T toCollectionMap(ResultSet rs, final Class collectionType, Class componentType, Class mapValType, int arrayMaxLength) { + return this.toCollectionMap(rs, collectionType, componentType, mapValType, arrayMaxLength, cal); + } + + public , E extends Map, V> T toCollectionMap(ResultSet rs, final Class collectionType, Class componentType, Class mapValType, Calendar cal) { + return this.toCollectionMap(rs, collectionType, componentType, mapValType, arrayMaxLength, cal); + } + + public , E extends Map, V> T toCollectionMap(ResultSet rs, T list, Class componentType, Class mapValType) { + return this.toCollectionMap(rs, list, componentType, mapValType, arrayMaxLength, cal); + } + + public , E extends Map, V> T toCollectionMap(ResultSet rs, T list, Class componentType, Class mapValType, int arrayMaxLength) { + return this.toCollectionMap(rs, list, componentType, mapValType, arrayMaxLength, cal); + } + + public , E extends Map, V> T toCollectionMap(ResultSet rs, T list, Class componentType, Class mapValType, Calendar cal) { + return this.toCollectionMap(rs, list, componentType, mapValType, arrayMaxLength, cal); + } + + public , K, E extends Map, V> T toMapMap(ResultSet rs, final Class returnType, Class mapKeyType, Class componentType, Class mapValType) { + return this.toMapMap(rs, returnType, mapKeyType, componentType, mapValType, arrayMaxLength, cal); + } + + public , K, E extends Map, V> T toMapMap(ResultSet rs, final Class returnType, Class mapKeyType, Class componentType, Class mapValType, int arrayMaxLength) { + return this.toMapMap(rs, returnType, mapKeyType, componentType, mapValType, arrayMaxLength, cal); + } + + public , K, E extends Map, V> T toMapMap(ResultSet rs, final Class returnType, Class mapKeyType, Class componentType, Class mapValType, Calendar cal) { + return this.toMapMap(rs, returnType, mapKeyType, componentType, mapValType, arrayMaxLength, cal); + } + + public , K, E extends Map, V> T toMapMap(ResultSet rs, T map, Class mapKeyType, Class componentType, Class mapValType) { + return this.toMapMap(rs, map, mapKeyType, componentType, mapValType, arrayMaxLength, cal); + } + + public , K, E extends Map, V> T toMapMap(ResultSet rs, T map, Class mapKeyType, Class componentType, Class mapValType, int arrayMaxLength) { + return this.toMapMap(rs, map, mapKeyType, componentType, mapValType, arrayMaxLength, cal); + } + + public , K, E extends Map, V> T toMapMap(ResultSet rs, T map, Class mapKeyType, Class componentType, Class mapValType, Calendar cal) { + return this.toMapMap(rs, map, mapKeyType, componentType, mapValType, arrayMaxLength, cal); + } + + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(ResultSet rs, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, Class mapValType) { + return this.toMapCollectionMap(rs, returnType, mapKeyType, collectionType, componentType, mapValType, arrayMaxLength, cal); + } + + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(ResultSet rs, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, int arrayMaxLength) { + return this.toMapCollectionMap(rs, returnType, mapKeyType, collectionType, componentType, mapValType, arrayMaxLength, cal); + } + + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(ResultSet rs, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, Calendar cal) { + return this.toMapCollectionMap(rs, returnType, mapKeyType, collectionType, componentType, mapValType, arrayMaxLength, cal); + } + + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(ResultSet rs, T map, Class mapKeyType, Class collectionType, Class componentType, Class mapValType) { + return this.toMapCollectionMap(rs, map, mapKeyType, collectionType, componentType, mapValType, arrayMaxLength, cal); + } + + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(ResultSet rs, T map, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, int arrayMaxLength) { + return this.toMapCollectionMap(rs, map, mapKeyType, collectionType, componentType, mapValType, arrayMaxLength, cal); + } + + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(ResultSet rs, T map, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, Calendar cal) { + return this.toMapCollectionMap(rs, map, mapKeyType, collectionType, componentType, mapValType, arrayMaxLength, cal); + } + + public , V> ListIterator> toListIteratorMap(ResultSet rs, final Class type, Class mapValType) { + return this.toListIteratorMap(rs, type, mapValType, arrayMaxLength, cal); + } + + public , V> ListIterator> toListIteratorMap(ResultSet rs, final Class type, Class mapValType, int arrayMaxLength) { + return this.toListIteratorMap(rs, type, mapValType, arrayMaxLength, cal); + } + + public , V> ListIterator> toListIteratorMap(ResultSet rs, final Class type, Class mapValType, Calendar cal) { + return this.toListIteratorMap(rs, type, mapValType, arrayMaxLength, cal); + } + + public , V> Iterator> toIteratorMap(ResultSet rs, final Class type, Class mapValType) { + return this.toIteratorMap(rs, type, mapValType, arrayMaxLength, cal); + } + + public , V> Iterator> toIteratorMap(ResultSet rs, final Class type, Class mapValType, int arrayMaxLength) { + return this.toIteratorMap(rs, type, mapValType, arrayMaxLength, cal); + } + + public , V> Iterator> toIteratorMap(ResultSet rs, final Class type, Class mapValType, Calendar cal) { + return this.toIteratorMap(rs, type, mapValType, arrayMaxLength, cal); + } + + public , V> Map[] toArrayMap(ResultSet rs, final Class type, Class mapValType) { + return this.toArrayMap(rs, type, mapValType, arrayMaxLength, cal); + } + + public , V> Map[] toArrayMap(ResultSet rs, final Class type, Class mapValType, int arrayMaxLength) { + return this.toArrayMap(rs, type, mapValType, arrayMaxLength, cal); + } + + public , V> Map[] toArrayMap(ResultSet rs, final Class type, Class mapValType, Calendar cal) { + return this.toArrayMap(rs, type, mapValType, arrayMaxLength, cal); + } + + public , V> List> toListMap(ResultSet rs, Class componentType, Class mapValType) { + return this.toListMap(rs, componentType, mapValType, arrayMaxLength, cal); + } + + public , V> List> toListMap(ResultSet rs, Class componentType, Class mapValType, int arrayMaxLength) { + return this.toListMap(rs, componentType, mapValType, arrayMaxLength, cal); + } + + public , V> List> toListMap(ResultSet rs, Class componentType, Class mapValType, Calendar cal) { + return this.toListMap(rs, componentType, mapValType, arrayMaxLength, cal); + } + + public , V> Map> toMapMap(ResultSet rs, Class mapKeyType, Class componentType, Class mapValType) { + return this.toMapMap(rs, mapKeyType, componentType, mapValType, arrayMaxLength, cal); + } + + public , V> Map> toMapMap(ResultSet rs, Class mapKeyType, Class componentType, Class mapValType, int arrayMaxLength) { + return this.toMapMap(rs, mapKeyType, componentType, mapValType, arrayMaxLength, cal); + } + + public , V> Map> toMapMap(ResultSet rs, Class mapKeyType, Class componentType, Class mapValType, Calendar cal) { + return this.toMapMap(rs, mapKeyType, componentType, mapValType, arrayMaxLength, cal); + } + + public , V> Map>> toMapListMap(ResultSet rs, Class mapKeyType, Class componentType, Class mapValType) { + return this.toMapListMap(rs, mapKeyType, componentType, mapValType, arrayMaxLength, cal); + } + + public , V> Map>> toMapListMap(ResultSet rs, Class mapKeyType, Class componentType, Class mapValType, int arrayMaxLength) { + return this.toMapListMap(rs, mapKeyType, componentType, mapValType, arrayMaxLength, cal); + } + + public , V> Map>> toMapListMap(ResultSet rs, Class mapKeyType, Class componentType, Class mapValType, Calendar cal) { + return this.toMapListMap(rs, mapKeyType, componentType, mapValType, arrayMaxLength, cal); + } + + public ListIterator> toListIteratorMap(ResultSet rs, Class mapValType) { + return this.toListIteratorMap(rs, mapValType, arrayMaxLength, cal); + } + + public ListIterator> toListIteratorMap(ResultSet rs, Class mapValType, int arrayMaxLength) { + return this.toListIteratorMap(rs, mapValType, arrayMaxLength, cal); + } + + public ListIterator> toListIteratorMap(ResultSet rs, Class mapValType, Calendar cal) { + return this.toListIteratorMap(rs, mapValType, arrayMaxLength, cal); + } + + public Iterator> toIteratorMap(ResultSet rs, Class mapValType) { + return this.toIteratorMap(rs, mapValType, arrayMaxLength, cal); + } + + public Iterator> toIteratorMap(ResultSet rs, Class mapValType, int arrayMaxLength) { + return this.toIteratorMap(rs, mapValType, arrayMaxLength, cal); + } + + public Iterator> toIteratorMap(ResultSet rs, Class mapValType, Calendar cal) { + return this.toIteratorMap(rs, mapValType, arrayMaxLength, cal); + } + + public List> toListMap(ResultSet rs, Class mapValType) { + return this.toListMap(rs, mapValType, arrayMaxLength, cal); + } + + public List> toListMap(ResultSet rs, Class mapValType, int arrayMaxLength) { + return this.toListMap(rs, mapValType, arrayMaxLength, cal); + } + + public List> toListMap(ResultSet rs, Class mapValType, Calendar cal) { + return this.toListMap(rs, mapValType, arrayMaxLength, cal); + } + + public Map> toMapMap(ResultSet rs, Class mapKeyType, Class mapValType) { + return this.toMapMap(rs, mapKeyType, mapValType, arrayMaxLength, cal); + } + + public Map> toMapMap(ResultSet rs, Class mapKeyType, Class mapValType, int arrayMaxLength) { + return this.toMapMap(rs, mapKeyType, mapValType, arrayMaxLength, cal); + } + + public Map> toMapMap(ResultSet rs, Class mapKeyType, Class mapValType, Calendar cal) { + return this.toMapMap(rs, mapKeyType, mapValType, arrayMaxLength, cal); + } + + public Map>> toMapListMap(ResultSet rs, Class mapKeyType, Class mapValType) { + return this.toMapListMap(rs, mapKeyType, mapValType, arrayMaxLength, cal); + } + + public Map>> toMapListMap(ResultSet rs, Class mapKeyType, Class mapValType, int arrayMaxLength) { + return this.toMapListMap(rs, mapKeyType, mapValType, arrayMaxLength, cal); + } + + public Map>> toMapListMap(ResultSet rs, Class mapKeyType, Class mapValType, Calendar cal) { + return this.toMapListMap(rs, mapKeyType, mapValType, arrayMaxLength, cal); + } + +} + diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowMapper.java new file mode 100644 index 0000000..a798111 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowMapper.java @@ -0,0 +1,240 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Header:$ + */ + +package com.moparisthebest.jdbc; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.Calendar; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Abstract base class for all row mappers. + * + * RowMappers are used to map the contents of a row in a ResultSet to the return type of an annotated method. + * Supported RowMapper types include: HashMap, Map, Object, XmlObject. When a ResultSetMapper is ready to + * map a ResultSet row to an object, it requests a RowMapper for the return type of the method from the + * RowMapperFactory. + * + */ +public abstract class RowMapper { + + private static final String SETTER_NAME_REGEX = "^(set)([A-Z_]\\w*+)"; + protected static final TypeMappingsFactory _tmf = TypeMappingsFactory.getInstance(); + protected static final Pattern _setterRegex = Pattern.compile(SETTER_NAME_REGEX); + + /** ResultSet to map. */ + protected final ResultSet _resultSet; + + /** Calendar instance for date/time mappings. */ + protected final Calendar _cal; + + /** Class to map ResultSet Rows to. */ + protected final Class _returnTypeClass; + + /** + * Create a new RowMapper for the specified ResultSet and return type Class. + * @param resultSet ResultSet to map + * @param returnTypeClass Class to map ResultSet rows to. + * @param cal Calendar instance for date/time values. + */ + protected RowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal) { + _resultSet = resultSet; + _returnTypeClass = returnTypeClass; + _cal = cal; + } + + /** + * Map a ResultSet row to the return type class + * @return An instance of class. + */ + public abstract Object mapRowToReturnType(); + + + /** + * Build a String array of column names from the ResultSet. + * @return A String array containing the column names contained within the ResultSet. + * @throws java.sql.SQLException on error + */ + protected String[] getKeysFromResultSet() throws SQLException { + + String[] keys; + final ResultSetMetaData md = _resultSet.getMetaData(); + final int columnCount = md.getColumnCount(); + + keys = new String[columnCount + 1]; + for (int i = 1; i <= columnCount; i++) { + keys[i] = md.getColumnName(i).toUpperCase(); + } + return keys; + } + + /** + * Determine if the given method is a java bean setter method. + * @param method Method to check + * @return True if the method is a setter method. + */ + protected boolean isSetterMethod(Method method) { + Matcher matcher = _setterRegex.matcher(method.getName()); + if (matcher.matches()) { + + if (Modifier.isStatic(method.getModifiers())) return false; + if (!Modifier.isPublic(method.getModifiers())) return false; + if (!Void.TYPE.equals(method.getReturnType())) return false; + + // method parameter checks + Class[] params = method.getParameterTypes(); + if (params.length != 1) return false; + if (TypeMappingsFactory.TYPE_UNKNOWN == _tmf.getTypeId(params[0])) return false; + + return true; + } + return false; + } + + /** + * Extract a column value from the ResultSet and return it as resultType. + * + * @param index The column index of the value to extract from the ResultSet. + * @param resultType The return type. Defined in TypeMappingsFactory. + * @return The extracted value + * @throws java.sql.SQLException on error. + */ + protected Object extractColumnValue(int index, int resultType) throws SQLException { + + switch (resultType) { + case TypeMappingsFactory.TYPE_INT: + return new Integer(_resultSet.getInt(index)); + case TypeMappingsFactory.TYPE_LONG: + return new Long(_resultSet.getLong(index)); + case TypeMappingsFactory.TYPE_FLOAT: + return new Float(_resultSet.getFloat(index)); + case TypeMappingsFactory.TYPE_DOUBLE: + return new Double(_resultSet.getDouble(index)); + case TypeMappingsFactory.TYPE_BYTE: + return new Byte(_resultSet.getByte(index)); + case TypeMappingsFactory.TYPE_SHORT: + return new Short(_resultSet.getShort(index)); + case TypeMappingsFactory.TYPE_BOOLEAN: + return _resultSet.getBoolean(index) ? Boolean.TRUE : Boolean.FALSE; + case TypeMappingsFactory.TYPE_INT_OBJ: + { + int i = _resultSet.getInt(index); + return _resultSet.wasNull() ? null : new Integer(i); + } + case TypeMappingsFactory.TYPE_LONG_OBJ: + { + long i = _resultSet.getLong(index); + return _resultSet.wasNull() ? null : new Long(i); + } + case TypeMappingsFactory.TYPE_FLOAT_OBJ: + { + float i = _resultSet.getFloat(index); + return _resultSet.wasNull() ? null : new Float(i); + } + case TypeMappingsFactory.TYPE_DOUBLE_OBJ: + { + double i = _resultSet.getDouble(index); + return _resultSet.wasNull() ? null : new Double(i); + } + case TypeMappingsFactory.TYPE_BYTE_OBJ: + { + byte i = _resultSet.getByte(index); + return _resultSet.wasNull() ? null : new Byte(i); + } + case TypeMappingsFactory.TYPE_SHORT_OBJ: + { + short i = _resultSet.getShort(index); + return _resultSet.wasNull() ? null : new Short(i); + } + case TypeMappingsFactory.TYPE_BOOLEAN_OBJ: + { + boolean i = _resultSet.getBoolean(index); + return _resultSet.wasNull() ? null : (i ? Boolean.TRUE : Boolean.FALSE); + } + case TypeMappingsFactory.TYPE_STRING: + case TypeMappingsFactory.TYPE_XMLBEAN_ENUM: + return _resultSet.getString(index); + case TypeMappingsFactory.TYPE_BIG_DECIMAL: + return _resultSet.getBigDecimal(index); + case TypeMappingsFactory.TYPE_BYTES: + return _resultSet.getBytes(index); + case TypeMappingsFactory.TYPE_TIMESTAMP: + { + if (null == _cal) + return _resultSet.getTimestamp(index); + else + return _resultSet.getTimestamp(index, _cal); + } + case TypeMappingsFactory.TYPE_TIME: + { + if (null == _cal) + return _resultSet.getTime(index); + else + return _resultSet.getTime(index, _cal); + } + case TypeMappingsFactory.TYPE_SQLDATE: + { + if (null == _cal) + return _resultSet.getDate(index); + else + return _resultSet.getDate(index, _cal); + } + case TypeMappingsFactory.TYPE_DATE: + { + // convert explicity to java.util.Date + // 12918 | knex does not return java.sql.Date properly from web service + java.sql.Timestamp ts = (null == _cal) ? _resultSet.getTimestamp(index) : _resultSet.getTimestamp(index, _cal); + if (null == ts) + return null; + return new java.util.Date(ts.getTime()); + } + case TypeMappingsFactory.TYPE_CALENDAR: + { + java.sql.Timestamp ts = (null == _cal) ? _resultSet.getTimestamp(index) : _resultSet.getTimestamp(index, _cal); + if (null == ts) + return null; + Calendar c = (null == _cal) ? Calendar.getInstance() : (Calendar) _cal.clone(); + c.setTimeInMillis(ts.getTime()); + return c; + } + case TypeMappingsFactory.TYPE_REF: + return _resultSet.getRef(index); + case TypeMappingsFactory.TYPE_BLOB: + return _resultSet.getBlob(index); + case TypeMappingsFactory.TYPE_CLOB: + return _resultSet.getClob(index); + case TypeMappingsFactory.TYPE_ARRAY: + return _resultSet.getArray(index); + case TypeMappingsFactory.TYPE_READER: + case TypeMappingsFactory.TYPE_STREAM: + throw new MapperException("streaming return types are not supported by the JdbcControl; use ResultSet instead"); + case TypeMappingsFactory.TYPE_STRUCT: + case TypeMappingsFactory.TYPE_UNKNOWN: + // JAVA_TYPE (could be any), or REF + return _resultSet.getObject(index); + default: + throw new MapperException("internal error: unknown type ID: " + Integer.toString(resultType)); + } + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java new file mode 100644 index 0000000..b2fbe14 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java @@ -0,0 +1,426 @@ +package com.moparisthebest.jdbc; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Header:$ + */ + +import java.lang.reflect.*; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + +import static com.moparisthebest.jdbc.UpdateableDTO.YES; +import static com.moparisthebest.jdbc.UpdateableDTO.NO; + +/** + * Map a ResultSet row to an Object. This mapper uses Java reflection to perform the mapping. + *

+ * This class is modified from org.apache.beehive.controls.system.jdbc.RowToObjectMapper + *

+ * The column names are compared, case insensitive, and with and without underscores (_), to the set* methods + * and fields of the given class to map the fields. For example: + *

+ * USERID would prefer method setUserId(), and fall back to field userId if the method doesn't exist + * USER_ID would act the same as the above, but also additionally try to match method setUser_Id(), or field user_id + *

+ * First, this class will look for a constructor that takes a ResultSet as a parameter, if it finds one, it will + * instantiate it with that constructor, sending in the ResultSet. Otherwise, this will only try to use public + * setters, but will set fields regardless of specified access, even private fields. + * + * @author Travis Burtrum (modifications from beehive) + */ +public class RowToObjectMapper extends RowMapper { + + public static final int TYPE_BOOLEAN = _tmf.getTypeId(Boolean.TYPE);//TypeMappingsFactory.TYPE_BOOLEAN; // not public? + public static final int TYPE_BOOLEAN_OBJ = _tmf.getTypeId(Boolean.class);//TypeMappingsFactory.TYPE_BOOLEAN_OBJ; // not public? + + protected final int _columnCount; + protected final Constructor resultSetConstructor; + protected final Class _returnTypeClass; // over-ride non-generic version of this in super class + + // only non-null when _returnTypeClass is an array, or a map + protected final Class componentType; + protected final boolean returnMap; + + protected AccessibleObject[] _fields; + protected int[] _fieldTypes; + + protected final Object[] _args = new Object[1]; + + public RowToObjectMapper(ResultSet resultSet, Class returnTypeClass) { + this(resultSet, returnTypeClass, null, null); + } + + public RowToObjectMapper(ResultSet resultSet, Class returnTypeClass, Class mapValType) { + this(resultSet, returnTypeClass, null, mapValType); + } + + public RowToObjectMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal) { + this(resultSet, returnTypeClass, cal, null); + } + + /** + * Create a new RowToObjectMapper. + * + * @param resultSet ResultSet to map + * @param returnTypeClass Class to map to. + * @param cal Calendar instance for date/time mappings. + */ + public RowToObjectMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType) { + super(resultSet, returnTypeClass, cal); + returnMap = Map.class.isAssignableFrom(returnTypeClass); + if(returnMap){ + _returnTypeClass = ResultSetMapper.getConcreteClass(returnTypeClass, HashMap.class); + componentType = mapValType; + }else{ + _returnTypeClass = returnTypeClass; + // detect if we want an array back + componentType = returnTypeClass.getComponentType(); + } + + _fields = null; + + // detect if returnTypeClass has a constructor that takes a ResultSet, if so, our job couldn't be easier... + Constructor resultSetConstructor = null; + try { + resultSetConstructor = returnTypeClass.getConstructor(ResultSet.class); + if (!resultSetConstructor.isAccessible()) + resultSetConstructor.setAccessible(true); + } catch (Throwable e) { + // do nothing, no such constructor + } + this.resultSetConstructor = resultSetConstructor; + + try { + _columnCount = resultSet.getMetaData().getColumnCount(); + } catch (SQLException e) { + throw new MapperException("RowToObjectMapper: SQLException: " + e.getMessage(), e); + } + } + + /** + * Do the mapping. + * + * @return An object instance. + */ + @SuppressWarnings({"unchecked"}) + public T mapRowToReturnType() { + + if (resultSetConstructor != null) + try { + return resultSetConstructor.newInstance(_resultSet); + } catch (Throwable e) { + throw new MapperException(e.getClass().getName() + " when trying to create instance of : " + + _returnTypeClass.getName() + " sending in a ResultSet object as a parameter", e); + } + + if(returnMap) // we want a map + try { + final Map ret = (Map)_returnTypeClass.newInstance(); + final ResultSetMetaData md = _resultSet.getMetaData(); + final int columnLength = _columnCount+1; + if(componentType != null && componentType != Object.class){ // we want a specific value type + int typeId = _tmf.getTypeId(componentType); + for(int x = 1; x < columnLength; ++x) + ret.put(md.getColumnName(x).toLowerCase(), extractColumnValue(x, typeId)); + } else // we want a generic object type + for(int x = 1; x < columnLength; ++x) + ret.put(md.getColumnName(x).toLowerCase(), _resultSet.getObject(x)); + return _returnTypeClass.cast(ret); + } catch (Throwable e) { + throw new MapperException(e.getClass().getName() + " when trying to create a Map from a ResultSet row" + + ", all columns must be of the map value type", e); + } + else if(componentType != null) // we want an array + try { + final Object[] ret = (Object[])Array.newInstance(componentType, _columnCount); + final int typeId = _tmf.getTypeId(componentType); + for(int x = 0; x < _columnCount;) + ret[x] = extractColumnValue(++x, typeId); + return _returnTypeClass.cast(ret); + } catch (Throwable e) { + throw new MapperException(e.getClass().getName() + " when trying to create a " + + componentType.getName() + "[] from a ResultSet row, all columns must be of that type", e); + } + + + T resultObject = null; + + // if the ResultSet only contains a single column we may be able to map directly + // to the return type -- if so we don't need to build any structures to support + // mapping + if (_columnCount == 1) { + + final int typeId = _tmf.getTypeId(_returnTypeClass); + + try { + if (typeId != TypeMappingsFactory.TYPE_UNKNOWN) { + return _returnTypeClass.cast(extractColumnValue(1, typeId)); + } else { + // we still might want a single value (i.e. java.util.Date) + Object val = extractColumnValue(1, typeId); + if (_returnTypeClass.isAssignableFrom(val.getClass())) { + return _returnTypeClass.cast(val); + } + } + } catch (Exception e) { + throw new MapperException(e.getMessage(), e); + } + } + + if (_fields == null) { + try { + getFieldMappings(); + } catch (SQLException e) { + throw new MapperException(e.getMessage(), e); + } + } + + try { + resultObject = _returnTypeClass.newInstance(); + } catch (Throwable e) { + throw new MapperException(e.getClass().getName() + " when trying to create instance of : " + + _returnTypeClass.getName(), e); + } + + for (int i = 1; i < _fields.length; i++) { + AccessibleObject f = _fields[i]; + + try { + _args[0] = extractColumnValue(i, _fieldTypes[i]); + //System.out.printf("field: '%s' obj: '%s' fieldType: '%s'\n", _fields[i], _args[0], _fieldTypes[i]); + // custom hacked-in support for enums, can do better when we scrap org.apache.beehive.controls.system.jdbc.TypeMappingsFactory + if(_fieldTypes[i] == 0 && _args[0] instanceof String){ + Class fieldType = f instanceof Field ? ((Field)f).getType() : ((Method)f).getParameterTypes()[0]; + if(Enum.class.isAssignableFrom(fieldType)) + _args[0] = Enum.valueOf((Class)fieldType, (String)_args[0]); + } + if (f instanceof Field) { + ((Field) f).set(resultObject, _args[0]); + } else { + ((Method) f).invoke(resultObject, _args); + } + } catch (SQLException e) { + throw new MapperException(e.getMessage(), e); + } catch (IllegalArgumentException iae) { + + try { + ResultSetMetaData md = _resultSet.getMetaData(); + if (f instanceof Field) { + throw new MapperException("The declared Java type for field " + ((Field) f).getName() + + ((Field) f).getType().toString() + + " is incompatible with the SQL format of column " + i + " '" + md.getColumnName(i) + + "' (" + md.getColumnTypeName(i) + + ") which returns objects of type " + _args[0].getClass().getName()); + } else { + throw new MapperException("The declared Java type for method " + ((Method) f).getName() + + ((Method) f).getParameterTypes()[0].toString() + + " is incompatible with the SQL format of column " + i + " '" + md.getColumnName(i) + + "' (" + md.getColumnTypeName(i) + + ") which returns objects of type " + _args[0].getClass().getName()); + } + } catch (SQLException e) { + throw new MapperException(e.getMessage(), e); + } + + } catch (IllegalAccessException e) { + if (f instanceof Field) { + throw new MapperException("IllegalAccessException when trying to access field " + ((Field) f).getName(), e); + } else { + throw new MapperException("IllegalAccessException when trying to access method " + ((Method) f).getName(), e); + } + } catch (InvocationTargetException e) { + throw new MapperException("InvocationTargetException when trying to access method " + ((Method) f).getName(), e); + } + } + // if this resultObject is Finishable, call finish() + if (resultObject instanceof Finishable) + try { + ((Finishable) resultObject).finish(_resultSet); + } catch (SQLException e) { + throw new MapperException(e.getMessage(), e); + } + return resultObject; + } + + /** + * Provided so we can extract a column value to use for the key of a map + * + * @param index + * @param classType + * @return + * @throws SQLException + */ + @SuppressWarnings({"unchecked"}) + public E extractColumnValue(int index, Class classType) throws SQLException { + return classType.cast(extractColumnValue(index, _tmf.getTypeId(classType))); + } + + /** + * Provided so we know whether to map all values to a type, or just the second one + * + * @return + */ + public int getColumnCount() { + return this._columnCount; + } + + /** + * Build the structures necessary to do the mapping + * + * @throws SQLException on error. + */ + protected void getFieldMappings() + throws SQLException { + + final String[] keys = getKeysFromResultSet(); + //System.out.println("keys: "+ Arrays.toString(keys)); + + // added this to handle stripping '_' from keys + Map strippedKeys = new HashMap(); + for (final String key : keys) { + String strippedKey = key; + if (key != null) { + strippedKey = key.replaceAll("_", ""); + if (key.equals(strippedKey)) + continue; + strippedKeys.put(strippedKey, key); + } + } + //System.out.println("strippedKeys: "+strippedKeys); + // + // find fields or setters for return class + // + HashMap mapFields = new HashMap(_columnCount * 2); + for (int i = 1; i <= _columnCount; i++) { + mapFields.put(keys[i], null); + } + + // public methods + Method[] classMethods = _returnTypeClass.getMethods(); + for (Method m : classMethods) { + //System.out.printf("method: '%s', isSetterMethod: '%s'\n", m, isSetterMethod(m)); + if (isSetterMethod(m)) { + String fieldName = m.getName().substring(3).toUpperCase(); + //System.out.println("METHOD-fieldName: "+fieldName); + if (!mapFields.containsKey(fieldName)) { + fieldName = strippedKeys.get(fieldName); + if (fieldName == null) + continue; + } + // check for overloads + Object field = mapFields.get(fieldName); + if (field == null) { + mapFields.put(fieldName, m); + } else { + throw new MapperException("Unable to choose between overloaded methods " + m.getName() + + " on the " + _returnTypeClass.getName() + " class. Mapping is done using " + + "a case insensitive comparison of SQL ResultSet columns to field " + + "names and public setter methods on the return class. Columns are also " + + "stripped of '_' and compared if no match is found with them."); + } + } + } + + // fix for 8813: include inherited and non-public fields + for (Class clazz = _returnTypeClass; clazz != null && clazz != Object.class; clazz = clazz.getSuperclass()) { + //System.out.println("searching for fields in class: "+clazz.getName()); + Field[] classFields = clazz.getDeclaredFields(); + //System.out.println("fields in class: "+Arrays.toString(classFields)); + for (Field f : classFields) { + if (Modifier.isStatic(f.getModifiers())) continue; + //if (!Modifier.isPublic(f.getModifiers())) continue; // commented this out to work on all types of fields + String fieldName = f.getName().toUpperCase(); + //System.out.println("fieldName: "+fieldName); + if (!mapFields.containsKey(fieldName)) { + fieldName = strippedKeys.get(fieldName); + if (fieldName == null) + continue; + } + Object field = mapFields.get(fieldName); + if (field == null) { + mapFields.put(fieldName, f); + } + } + } + + // finally actually init the fields array + _fields = new AccessibleObject[_columnCount + 1]; + _fieldTypes = new int[_columnCount + 1]; + + for (int i = 1; i < _fields.length; i++) { + AccessibleObject f = mapFields.get(keys[i]); + if (f == null) { + throw new MapperException("Unable to map the SQL column " + keys[i] + + " to a field on the " + _returnTypeClass.getName() + + " class. Mapping is done using a case insensitive comparision of SQL ResultSet " + + "columns to field " + + "names and public setter methods on the return class. Columns are also " + + "stripped of '_' and compared if no match is found with them."); + } + f.setAccessible(true); + _fields[i] = f; + if (f instanceof Field) { + _fieldTypes[i] = _tmf.getTypeId(((Field) f).getType()); + } else { + _fieldTypes[i] = _tmf.getTypeId(((Method) f).getParameterTypes()[0]); + } + } + } + + @Override + protected Object extractColumnValue(int index, int resultType) throws SQLException { + try{ + if (resultType != TYPE_BOOLEAN && resultType != TYPE_BOOLEAN_OBJ) + return super.extractColumnValue(index, resultType); + else { + // do some special handling to convert a database string to a boolean + boolean ret; + try { + // try to get an actual boolean from the database + ret = _resultSet.getBoolean(index); + // null seems to get returned as false above, so String code won't run if its null + if (_resultSet.wasNull()) + if (resultType == TYPE_BOOLEAN_OBJ) + return null; // only return null for Boolean object + else + throw new MapperException(String.format("Implicit conversion of database string to boolean failed on column '%d'. Returned string needs to be 'Y' or 'N' and was instead 'null'. If you want to accept null values, make it an object Boolean instead of primitive boolean.", index)); + } catch (SQLException e) { + // if we are here, it wasn't a boolean or null, so try to grab a string instead + String bool = _resultSet.getString(index);//.toUpperCase(); // do we want it case-insensitive? + ret = YES.equals(bool); + if (!ret && !NO.equals(bool)) + throw new MapperException(String.format("Implicit conversion of database string to boolean failed on column '%d'. Returned string needs to be 'Y' or 'N' and was instead '%s'.", index, bool)); + //throw e; + } + return ret ? Boolean.TRUE : Boolean.FALSE; + } + }catch(SQLException e){ + throw new SQLExceptionColumnNum(e, index); + } + } + + public static T fixNull(Class returnType) { + return returnType.cast(_tmf.fixNull(returnType)); + } +} + diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/SQLExceptionColumnNum.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/SQLExceptionColumnNum.java new file mode 100644 index 0000000..960fa23 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/SQLExceptionColumnNum.java @@ -0,0 +1,17 @@ +package com.moparisthebest.jdbc; + +import java.sql.SQLException; + +public class SQLExceptionColumnNum extends SQLException { + + private final int columnNum; + + public SQLExceptionColumnNum(Throwable cause, int columnNum) { + super("At column number "+columnNum, cause); + this.columnNum = columnNum; + } + + public int getColumnNum() { + return columnNum; + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/StatementClosingResultSet.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/StatementClosingResultSet.java new file mode 100644 index 0000000..2cbc3a9 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/StatementClosingResultSet.java @@ -0,0 +1,989 @@ +package com.moparisthebest.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; +import java.util.Calendar; +import java.util.Map; + +/** + * This does exactly what you would expect, delegate to a ResultSet and close a Statement object when you close this ResultSet + */ +@SuppressWarnings("deprecation") +public class StatementClosingResultSet implements ResultSet { + + private final ResultSet delegate; + private final Statement statement; + + public StatementClosingResultSet(ResultSet delegate, Statement statement) { + this.delegate = delegate; + this.statement = statement; + } + + /** + * This is the only method here that doesn't ONLY delegate + * + * @throws SQLException + */ + @Override + public void close() throws SQLException { + delegate.close(); + if (statement != null) + statement.close(); + } + + @Override + public boolean next() throws SQLException { + return delegate.next(); + } + + @Override + public boolean wasNull() throws SQLException { + return delegate.wasNull(); + } + + @Override + public String getString(int columnIndex) throws SQLException { + return delegate.getString(columnIndex); + } + + @Override + public boolean getBoolean(int columnIndex) throws SQLException { + return delegate.getBoolean(columnIndex); + } + + @Override + public byte getByte(int columnIndex) throws SQLException { + return delegate.getByte(columnIndex); + } + + @Override + public short getShort(int columnIndex) throws SQLException { + return delegate.getShort(columnIndex); + } + + @Override + public int getInt(int columnIndex) throws SQLException { + return delegate.getInt(columnIndex); + } + + @Override + public long getLong(int columnIndex) throws SQLException { + return delegate.getLong(columnIndex); + } + + @Override + public float getFloat(int columnIndex) throws SQLException { + return delegate.getFloat(columnIndex); + } + + @Override + public double getDouble(int columnIndex) throws SQLException { + return delegate.getDouble(columnIndex); + } + + @Override + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { + return delegate.getBigDecimal(columnIndex, scale); + } + + @Override + public byte[] getBytes(int columnIndex) throws SQLException { + return delegate.getBytes(columnIndex); + } + + @Override + public Date getDate(int columnIndex) throws SQLException { + return delegate.getDate(columnIndex); + } + + @Override + public Time getTime(int columnIndex) throws SQLException { + return delegate.getTime(columnIndex); + } + + @Override + public Timestamp getTimestamp(int columnIndex) throws SQLException { + return delegate.getTimestamp(columnIndex); + } + + @Override + public InputStream getAsciiStream(int columnIndex) throws SQLException { + return delegate.getAsciiStream(columnIndex); + } + + @Override + public InputStream getUnicodeStream(int columnIndex) throws SQLException { + return delegate.getUnicodeStream(columnIndex); + } + + @Override + public InputStream getBinaryStream(int columnIndex) throws SQLException { + return delegate.getBinaryStream(columnIndex); + } + + @Override + public String getString(String columnLabel) throws SQLException { + return delegate.getString(columnLabel); + } + + @Override + public boolean getBoolean(String columnLabel) throws SQLException { + return delegate.getBoolean(columnLabel); + } + + @Override + public byte getByte(String columnLabel) throws SQLException { + return delegate.getByte(columnLabel); + } + + @Override + public short getShort(String columnLabel) throws SQLException { + return delegate.getShort(columnLabel); + } + + @Override + public int getInt(String columnLabel) throws SQLException { + return delegate.getInt(columnLabel); + } + + @Override + public long getLong(String columnLabel) throws SQLException { + return delegate.getLong(columnLabel); + } + + @Override + public float getFloat(String columnLabel) throws SQLException { + return delegate.getFloat(columnLabel); + } + + @Override + public double getDouble(String columnLabel) throws SQLException { + return delegate.getDouble(columnLabel); + } + + @Override + public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { + return delegate.getBigDecimal(columnLabel, scale); + } + + @Override + public byte[] getBytes(String columnLabel) throws SQLException { + return delegate.getBytes(columnLabel); + } + + @Override + public Date getDate(String columnLabel) throws SQLException { + return delegate.getDate(columnLabel); + } + + @Override + public Time getTime(String columnLabel) throws SQLException { + return delegate.getTime(columnLabel); + } + + @Override + public Timestamp getTimestamp(String columnLabel) throws SQLException { + return delegate.getTimestamp(columnLabel); + } + + @Override + public InputStream getAsciiStream(String columnLabel) throws SQLException { + return delegate.getAsciiStream(columnLabel); + } + + @Override + public InputStream getUnicodeStream(String columnLabel) throws SQLException { + return delegate.getUnicodeStream(columnLabel); + } + + @Override + public InputStream getBinaryStream(String columnLabel) throws SQLException { + return delegate.getBinaryStream(columnLabel); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return delegate.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + delegate.clearWarnings(); + } + + @Override + public String getCursorName() throws SQLException { + return delegate.getCursorName(); + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + return delegate.getMetaData(); + } + + @Override + public Object getObject(int columnIndex) throws SQLException { + return delegate.getObject(columnIndex); + } + + @Override + public Object getObject(String columnLabel) throws SQLException { + return delegate.getObject(columnLabel); + } + + @Override + public int findColumn(String columnLabel) throws SQLException { + return delegate.findColumn(columnLabel); + } + + @Override + public Reader getCharacterStream(int columnIndex) throws SQLException { + return delegate.getCharacterStream(columnIndex); + } + + @Override + public Reader getCharacterStream(String columnLabel) throws SQLException { + return delegate.getCharacterStream(columnLabel); + } + + @Override + public BigDecimal getBigDecimal(int columnIndex) throws SQLException { + return delegate.getBigDecimal(columnIndex); + } + + @Override + public BigDecimal getBigDecimal(String columnLabel) throws SQLException { + return delegate.getBigDecimal(columnLabel); + } + + @Override + public boolean isBeforeFirst() throws SQLException { + return delegate.isBeforeFirst(); + } + + @Override + public boolean isAfterLast() throws SQLException { + return delegate.isAfterLast(); + } + + @Override + public boolean isFirst() throws SQLException { + return delegate.isFirst(); + } + + @Override + public boolean isLast() throws SQLException { + return delegate.isLast(); + } + + @Override + public void beforeFirst() throws SQLException { + delegate.beforeFirst(); + } + + @Override + public void afterLast() throws SQLException { + delegate.afterLast(); + } + + @Override + public boolean first() throws SQLException { + return delegate.first(); + } + + @Override + public boolean last() throws SQLException { + return delegate.last(); + } + + @Override + public int getRow() throws SQLException { + return delegate.getRow(); + } + + @Override + public boolean absolute(int row) throws SQLException { + return delegate.absolute(row); + } + + @Override + public boolean relative(int rows) throws SQLException { + return delegate.relative(rows); + } + + @Override + public boolean previous() throws SQLException { + return delegate.previous(); + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + delegate.setFetchDirection(direction); + } + + @Override + public int getFetchDirection() throws SQLException { + return delegate.getFetchDirection(); + } + + @Override + public void setFetchSize(int rows) throws SQLException { + delegate.setFetchSize(rows); + } + + @Override + public int getFetchSize() throws SQLException { + return delegate.getFetchSize(); + } + + @Override + public int getType() throws SQLException { + return delegate.getType(); + } + + @Override + public int getConcurrency() throws SQLException { + return delegate.getConcurrency(); + } + + @Override + public boolean rowUpdated() throws SQLException { + return delegate.rowUpdated(); + } + + @Override + public boolean rowInserted() throws SQLException { + return delegate.rowInserted(); + } + + @Override + public boolean rowDeleted() throws SQLException { + return delegate.rowDeleted(); + } + + @Override + public void updateNull(int columnIndex) throws SQLException { + delegate.updateNull(columnIndex); + } + + @Override + public void updateBoolean(int columnIndex, boolean x) throws SQLException { + delegate.updateBoolean(columnIndex, x); + } + + @Override + public void updateByte(int columnIndex, byte x) throws SQLException { + delegate.updateByte(columnIndex, x); + } + + @Override + public void updateShort(int columnIndex, short x) throws SQLException { + delegate.updateShort(columnIndex, x); + } + + @Override + public void updateInt(int columnIndex, int x) throws SQLException { + delegate.updateInt(columnIndex, x); + } + + @Override + public void updateLong(int columnIndex, long x) throws SQLException { + delegate.updateLong(columnIndex, x); + } + + @Override + public void updateFloat(int columnIndex, float x) throws SQLException { + delegate.updateFloat(columnIndex, x); + } + + @Override + public void updateDouble(int columnIndex, double x) throws SQLException { + delegate.updateDouble(columnIndex, x); + } + + @Override + public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { + delegate.updateBigDecimal(columnIndex, x); + } + + @Override + public void updateString(int columnIndex, String x) throws SQLException { + delegate.updateString(columnIndex, x); + } + + @Override + public void updateBytes(int columnIndex, byte[] x) throws SQLException { + delegate.updateBytes(columnIndex, x); + } + + @Override + public void updateDate(int columnIndex, Date x) throws SQLException { + delegate.updateDate(columnIndex, x); + } + + @Override + public void updateTime(int columnIndex, Time x) throws SQLException { + delegate.updateTime(columnIndex, x); + } + + @Override + public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { + delegate.updateTimestamp(columnIndex, x); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { + delegate.updateAsciiStream(columnIndex, x, length); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { + delegate.updateBinaryStream(columnIndex, x, length); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { + delegate.updateCharacterStream(columnIndex, x, length); + } + + @Override + public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException { + delegate.updateObject(columnIndex, x, scaleOrLength); + } + + @Override + public void updateObject(int columnIndex, Object x) throws SQLException { + delegate.updateObject(columnIndex, x); + } + + @Override + public void updateNull(String columnLabel) throws SQLException { + delegate.updateNull(columnLabel); + } + + @Override + public void updateBoolean(String columnLabel, boolean x) throws SQLException { + delegate.updateBoolean(columnLabel, x); + } + + @Override + public void updateByte(String columnLabel, byte x) throws SQLException { + delegate.updateByte(columnLabel, x); + } + + @Override + public void updateShort(String columnLabel, short x) throws SQLException { + delegate.updateShort(columnLabel, x); + } + + @Override + public void updateInt(String columnLabel, int x) throws SQLException { + delegate.updateInt(columnLabel, x); + } + + @Override + public void updateLong(String columnLabel, long x) throws SQLException { + delegate.updateLong(columnLabel, x); + } + + @Override + public void updateFloat(String columnLabel, float x) throws SQLException { + delegate.updateFloat(columnLabel, x); + } + + @Override + public void updateDouble(String columnLabel, double x) throws SQLException { + delegate.updateDouble(columnLabel, x); + } + + @Override + public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException { + delegate.updateBigDecimal(columnLabel, x); + } + + @Override + public void updateString(String columnLabel, String x) throws SQLException { + delegate.updateString(columnLabel, x); + } + + @Override + public void updateBytes(String columnLabel, byte[] x) throws SQLException { + delegate.updateBytes(columnLabel, x); + } + + @Override + public void updateDate(String columnLabel, Date x) throws SQLException { + delegate.updateDate(columnLabel, x); + } + + @Override + public void updateTime(String columnLabel, Time x) throws SQLException { + delegate.updateTime(columnLabel, x); + } + + @Override + public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException { + delegate.updateTimestamp(columnLabel, x); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException { + delegate.updateAsciiStream(columnLabel, x, length); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException { + delegate.updateBinaryStream(columnLabel, x, length); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException { + delegate.updateCharacterStream(columnLabel, reader, length); + } + + @Override + public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException { + delegate.updateObject(columnLabel, x, scaleOrLength); + } + + @Override + public void updateObject(String columnLabel, Object x) throws SQLException { + delegate.updateObject(columnLabel, x); + } + + @Override + public void insertRow() throws SQLException { + delegate.insertRow(); + } + + @Override + public void updateRow() throws SQLException { + delegate.updateRow(); + } + + @Override + public void deleteRow() throws SQLException { + delegate.deleteRow(); + } + + @Override + public void refreshRow() throws SQLException { + delegate.refreshRow(); + } + + @Override + public void cancelRowUpdates() throws SQLException { + delegate.cancelRowUpdates(); + } + + @Override + public void moveToInsertRow() throws SQLException { + delegate.moveToInsertRow(); + } + + @Override + public void moveToCurrentRow() throws SQLException { + delegate.moveToCurrentRow(); + } + + @Override + public Statement getStatement() throws SQLException { + return delegate.getStatement(); + } + + @Override + public Object getObject(int columnIndex, Map> map) throws SQLException { + return delegate.getObject(columnIndex, map); + } + + @Override + public Ref getRef(int columnIndex) throws SQLException { + return delegate.getRef(columnIndex); + } + + @Override + public Blob getBlob(int columnIndex) throws SQLException { + return delegate.getBlob(columnIndex); + } + + @Override + public Clob getClob(int columnIndex) throws SQLException { + return delegate.getClob(columnIndex); + } + + @Override + public Array getArray(int columnIndex) throws SQLException { + return delegate.getArray(columnIndex); + } + + @Override + public Object getObject(String columnLabel, Map> map) throws SQLException { + return delegate.getObject(columnLabel, map); + } + + @Override + public Ref getRef(String columnLabel) throws SQLException { + return delegate.getRef(columnLabel); + } + + @Override + public Blob getBlob(String columnLabel) throws SQLException { + return delegate.getBlob(columnLabel); + } + + @Override + public Clob getClob(String columnLabel) throws SQLException { + return delegate.getClob(columnLabel); + } + + @Override + public Array getArray(String columnLabel) throws SQLException { + return delegate.getArray(columnLabel); + } + + @Override + public Date getDate(int columnIndex, Calendar cal) throws SQLException { + return delegate.getDate(columnIndex, cal); + } + + @Override + public Date getDate(String columnLabel, Calendar cal) throws SQLException { + return delegate.getDate(columnLabel, cal); + } + + @Override + public Time getTime(int columnIndex, Calendar cal) throws SQLException { + return delegate.getTime(columnIndex, cal); + } + + @Override + public Time getTime(String columnLabel, Calendar cal) throws SQLException { + return delegate.getTime(columnLabel, cal); + } + + @Override + public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { + return delegate.getTimestamp(columnIndex, cal); + } + + @Override + public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { + return delegate.getTimestamp(columnLabel, cal); + } + + @Override + public URL getURL(int columnIndex) throws SQLException { + return delegate.getURL(columnIndex); + } + + @Override + public URL getURL(String columnLabel) throws SQLException { + return delegate.getURL(columnLabel); + } + + @Override + public void updateRef(int columnIndex, Ref x) throws SQLException { + delegate.updateRef(columnIndex, x); + } + + @Override + public void updateRef(String columnLabel, Ref x) throws SQLException { + delegate.updateRef(columnLabel, x); + } + + @Override + public void updateBlob(int columnIndex, Blob x) throws SQLException { + delegate.updateBlob(columnIndex, x); + } + + @Override + public void updateBlob(String columnLabel, Blob x) throws SQLException { + delegate.updateBlob(columnLabel, x); + } + + @Override + public void updateClob(int columnIndex, Clob x) throws SQLException { + delegate.updateClob(columnIndex, x); + } + + @Override + public void updateClob(String columnLabel, Clob x) throws SQLException { + delegate.updateClob(columnLabel, x); + } + + @Override + public void updateArray(int columnIndex, Array x) throws SQLException { + delegate.updateArray(columnIndex, x); + } + + @Override + public void updateArray(String columnLabel, Array x) throws SQLException { + delegate.updateArray(columnLabel, x); + } + + @Override + public RowId getRowId(int columnIndex) throws SQLException { + return delegate.getRowId(columnIndex); + } + + @Override + public RowId getRowId(String columnLabel) throws SQLException { + return delegate.getRowId(columnLabel); + } + + @Override + public void updateRowId(int columnIndex, RowId x) throws SQLException { + delegate.updateRowId(columnIndex, x); + } + + @Override + public void updateRowId(String columnLabel, RowId x) throws SQLException { + delegate.updateRowId(columnLabel, x); + } + + @Override + public int getHoldability() throws SQLException { + return delegate.getHoldability(); + } + + @Override + public boolean isClosed() throws SQLException { + return delegate.isClosed(); + } + + @Override + public void updateNString(int columnIndex, String nString) throws SQLException { + delegate.updateNString(columnIndex, nString); + } + + @Override + public void updateNString(String columnLabel, String nString) throws SQLException { + delegate.updateNString(columnLabel, nString); + } + + @Override + public void updateNClob(int columnIndex, NClob nClob) throws SQLException { + delegate.updateNClob(columnIndex, nClob); + } + + @Override + public void updateNClob(String columnLabel, NClob nClob) throws SQLException { + delegate.updateNClob(columnLabel, nClob); + } + + @Override + public NClob getNClob(int columnIndex) throws SQLException { + return delegate.getNClob(columnIndex); + } + + @Override + public NClob getNClob(String columnLabel) throws SQLException { + return delegate.getNClob(columnLabel); + } + + @Override + public SQLXML getSQLXML(int columnIndex) throws SQLException { + return delegate.getSQLXML(columnIndex); + } + + @Override + public SQLXML getSQLXML(String columnLabel) throws SQLException { + return delegate.getSQLXML(columnLabel); + } + + @Override + public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { + delegate.updateSQLXML(columnIndex, xmlObject); + } + + @Override + public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException { + delegate.updateSQLXML(columnLabel, xmlObject); + } + + @Override + public String getNString(int columnIndex) throws SQLException { + return delegate.getNString(columnIndex); + } + + @Override + public String getNString(String columnLabel) throws SQLException { + return delegate.getNString(columnLabel); + } + + @Override + public Reader getNCharacterStream(int columnIndex) throws SQLException { + return delegate.getNCharacterStream(columnIndex); + } + + @Override + public Reader getNCharacterStream(String columnLabel) throws SQLException { + return delegate.getNCharacterStream(columnLabel); + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + delegate.updateNCharacterStream(columnIndex, x, length); + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + delegate.updateNCharacterStream(columnLabel, reader, length); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { + delegate.updateAsciiStream(columnIndex, x, length); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { + delegate.updateBinaryStream(columnIndex, x, length); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + delegate.updateCharacterStream(columnIndex, x, length); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { + delegate.updateAsciiStream(columnLabel, x, length); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { + delegate.updateBinaryStream(columnLabel, x, length); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + delegate.updateCharacterStream(columnLabel, reader, length); + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { + delegate.updateBlob(columnIndex, inputStream, length); + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { + delegate.updateBlob(columnLabel, inputStream, length); + } + + @Override + public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { + delegate.updateClob(columnIndex, reader, length); + } + + @Override + public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { + delegate.updateClob(columnLabel, reader, length); + } + + @Override + public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { + delegate.updateNClob(columnIndex, reader, length); + } + + @Override + public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { + delegate.updateNClob(columnLabel, reader, length); + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { + delegate.updateNCharacterStream(columnIndex, x); + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { + delegate.updateNCharacterStream(columnLabel, reader); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { + delegate.updateAsciiStream(columnIndex, x); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { + delegate.updateBinaryStream(columnIndex, x); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { + delegate.updateCharacterStream(columnIndex, x); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { + delegate.updateAsciiStream(columnLabel, x); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { + delegate.updateBinaryStream(columnLabel, x); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { + delegate.updateCharacterStream(columnLabel, reader); + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { + delegate.updateBlob(columnIndex, inputStream); + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { + delegate.updateBlob(columnLabel, inputStream); + } + + @Override + public void updateClob(int columnIndex, Reader reader) throws SQLException { + delegate.updateClob(columnIndex, reader); + } + + @Override + public void updateClob(String columnLabel, Reader reader) throws SQLException { + delegate.updateClob(columnLabel, reader); + } + + @Override + public void updateNClob(int columnIndex, Reader reader) throws SQLException { + delegate.updateNClob(columnIndex, reader); + } + + @Override + public void updateNClob(String columnLabel, Reader reader) throws SQLException { + delegate.updateNClob(columnLabel, reader); + } + + // Java 7+ methods, implemented poorly using Java 6... :( + //@Override + public T getObject(int columnIndex, Class type) throws SQLException { + //return delegate.getObject(columnIndex, type); + return type.cast(delegate.getObject(columnIndex)); + } + + //@Override + public T getObject(String columnLabel, Class type) throws SQLException { + //return delegate.getObject(columnLabel, type); + return type.cast(delegate.getObject(columnLabel)); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return delegate.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return delegate.isWrapperFor(iface); + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/TryClose.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/TryClose.java new file mode 100644 index 0000000..d401dc4 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/TryClose.java @@ -0,0 +1,60 @@ +package com.moparisthebest.jdbc; + +import javax.naming.Context; +import java.io.Closeable; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.Statement; + +public class TryClose { // tries to close certain object types + + public static void tryClose(ResultSet obj) { + if (obj == null) + return; + try { + obj.close(); + } catch (Throwable e) { + // ignore... + } + } + + public static void tryClose(Context obj) { + if (obj == null) + return; + try { + obj.close(); + } catch (Throwable e) { + // ignore... + } + } + + public static void tryClose(Connection obj) { + if (obj == null) + return; + try { + obj.close(); + } catch (Throwable e) { + // ignore... + } + } + + public static void tryClose(Statement obj) { + if (obj == null) + return; + try { + obj.close(); + } catch (Throwable e) { + // ignore... + } + } + + public static void tryClose(Closeable obj) { + if (obj == null) + return; + try { + obj.close(); + } catch (Throwable e) { + // ignore... + } + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/TypeMappingsFactory.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/TypeMappingsFactory.java new file mode 100644 index 0000000..fa3890b --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/TypeMappingsFactory.java @@ -0,0 +1,403 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Header:$ + */ +package com.moparisthebest.jdbc; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Types; +import java.util.HashMap; +import java.util.Map; + +/** + * Currently contains all types of type mappings. Implemented using singleton pattern. + */ +public final class TypeMappingsFactory { + + /* @todo: refactor! */ + + private static TypeMappingsFactory _instance; + private static Class XMLBEANS_STRING_ENUM_ABSTRACT_BASE = null; + + static { + try { + XMLBEANS_STRING_ENUM_ABSTRACT_BASE = Class.forName("org.apache.xmlbeans.StringEnumAbstractBase"); + } catch (ClassNotFoundException e) { + // not an error, just means XmlBeans is not available + } + } + + /** + * Get an instance of this class. + * @return TypeMappingsFactory instance. + */ + public static TypeMappingsFactory getInstance() { + if (_instance == null) { + _instance = new TypeMappingsFactory(); + } + return _instance; + } + + public static final int TYPE_UNKNOWN = 0; + static final int TYPE_BYTE = 1; + static final int TYPE_SHORT = 2; + static final int TYPE_INT = 3; + static final int TYPE_LONG = 4; + static final int TYPE_FLOAT = 5; + static final int TYPE_DOUBLE = 6; + static final int TYPE_BOOLEAN = 7; + static final int TYPE_BYTE_OBJ = 8; + static final int TYPE_SHORT_OBJ = 9; + static final int TYPE_INT_OBJ = 10; + static final int TYPE_LONG_OBJ = 11; + static final int TYPE_FLOAT_OBJ = 12; + static final int TYPE_DOUBLE_OBJ = 13; + static final int TYPE_BOOLEAN_OBJ = 14; + static final int TYPE_BIG_DECIMAL = 15; + static final int TYPE_STRING = 16; + static final int TYPE_BYTES = 17; + static final int TYPE_SQLDATE = 18; + static final int TYPE_TIME = 19; + static final int TYPE_TIMESTAMP = 20; + static final int TYPE_STREAM = 21; + static final int TYPE_READER = 22; + static final int TYPE_CLOB = 23; + static final int TYPE_BLOB = 24; + static final int TYPE_ARRAY = 25; + static final int TYPE_REF = 26; + static final int TYPE_DATE = 27; + static final int TYPE_CALENDAR = 28; + static final int TYPE_STRUCT = 29; + static final int TYPE_XMLBEAN_ENUM = 30; + static final int TYPE_MAX = 31; + + private Map _primitiveDefaults; + + // + // keys in this map are the class of the method's return type, + // values are the set of constants defined above all prefixed with + // TYPE_ + // + private Map _typeMap; + private Map _typeSqlMap; + + /** + * Map a string version of sql type to sql type (java.sql.Types). + * example: "INTEGER" maps to java.sql.Types.INTEGER + */ + private Map _typeSqlNameMap; + + private static Method _methodMapGet; + + /** + * Constructor + */ + TypeMappingsFactory() { + + _primitiveDefaults = new HashMap(); + _primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE); + _primitiveDefaults.put(Integer.TYPE, new Integer(0)); + _primitiveDefaults.put(Long.TYPE, new Long(0)); + _primitiveDefaults.put(Byte.TYPE, new Byte((byte) 0)); + _primitiveDefaults.put(Short.TYPE, new Short((short) 0)); + _primitiveDefaults.put(Character.TYPE, new Character('\u0000')); + _primitiveDefaults.put(Float.TYPE, new Float(0.0f)); + _primitiveDefaults.put(Double.TYPE, new Double(0.0d)); + + // Class to internal enum + _typeMap = new HashMap(TYPE_MAX * 2); + _typeMap.put(Boolean.TYPE, new Integer(TYPE_BOOLEAN)); + _typeMap.put(Integer.TYPE, new Integer(TYPE_INT)); + _typeMap.put(Long.TYPE, new Integer(TYPE_LONG)); + _typeMap.put(Byte.TYPE, new Integer(TYPE_BYTE)); + _typeMap.put(Short.TYPE, new Integer(TYPE_SHORT)); + _typeMap.put(Float.TYPE, new Integer(TYPE_FLOAT)); + _typeMap.put(Double.TYPE, new Integer(TYPE_DOUBLE)); + _typeMap.put(Boolean.class, new Integer(TYPE_BOOLEAN_OBJ)); + _typeMap.put(Integer.class, new Integer(TYPE_INT_OBJ)); + _typeMap.put(Long.class, new Integer(TYPE_LONG_OBJ)); + _typeMap.put(Byte.class, new Integer(TYPE_BYTE_OBJ)); + _typeMap.put(Short.class, new Integer(TYPE_SHORT_OBJ)); + _typeMap.put(Float.class, new Integer(TYPE_FLOAT_OBJ)); + _typeMap.put(Double.class, new Integer(TYPE_DOUBLE_OBJ)); + _typeMap.put(String.class, new Integer(TYPE_STRING)); + _typeMap.put(java.math.BigDecimal.class, new Integer(TYPE_BIG_DECIMAL)); + _typeMap.put(byte[].class, new Integer(TYPE_BYTES)); + _typeMap.put(java.sql.Timestamp.class, new Integer(TYPE_TIMESTAMP)); + _typeMap.put(java.sql.Time.class, new Integer(TYPE_TIME)); + _typeMap.put(java.sql.Date.class, new Integer(TYPE_SQLDATE)); + _typeMap.put(java.sql.Ref.class, new Integer(TYPE_REF)); + _typeMap.put(Blob.class, new Integer(TYPE_BLOB)); + _typeMap.put(Clob.class, new Integer(TYPE_CLOB)); + _typeMap.put(java.sql.Array.class, new Integer(TYPE_ARRAY)); + _typeMap.put(java.sql.Struct.class, new Integer(TYPE_STRUCT)); + _typeMap.put(java.io.Reader.class, new Integer(TYPE_READER)); + _typeMap.put(java.io.InputStream.class, new Integer(TYPE_STREAM)); + _typeMap.put(java.util.Date.class, new Integer(TYPE_DATE)); + _typeMap.put(java.util.Calendar.class, new Integer(TYPE_CALENDAR)); + _typeMap.put(java.util.GregorianCalendar.class, new Integer(TYPE_CALENDAR)); + if (XMLBEANS_STRING_ENUM_ABSTRACT_BASE != null) { + _typeMap.put(XMLBEANS_STRING_ENUM_ABSTRACT_BASE, new Integer(TYPE_XMLBEAN_ENUM)); + } + + // Class to java.sql.Types + _typeSqlMap = new HashMap(TYPE_MAX * 2); + _typeSqlMap.put(Boolean.TYPE, new Integer(Types.BOOLEAN)); + _typeSqlMap.put(Integer.TYPE, new Integer(Types.INTEGER)); + _typeSqlMap.put(Long.TYPE, new Integer(Types.BIGINT)); + _typeSqlMap.put(Byte.TYPE, new Integer(Types.TINYINT)); + _typeSqlMap.put(Short.TYPE, new Integer(Types.SMALLINT)); + _typeSqlMap.put(Float.TYPE, new Integer(Types.REAL)); + _typeSqlMap.put(Double.TYPE, new Integer(Types.DOUBLE)); + _typeSqlMap.put(Boolean.class, new Integer(Types.BOOLEAN)); + _typeSqlMap.put(Integer.class, new Integer(Types.INTEGER)); + _typeSqlMap.put(Long.class, new Integer(Types.BIGINT)); + _typeSqlMap.put(Byte.class, new Integer(Types.TINYINT)); + _typeSqlMap.put(Short.class, new Integer(Types.SMALLINT)); + _typeSqlMap.put(Float.class, new Integer(Types.REAL)); + _typeSqlMap.put(Double.class, new Integer(Types.DOUBLE)); + _typeSqlMap.put(String.class, new Integer(Types.VARCHAR)); + _typeSqlMap.put(java.math.BigDecimal.class, new Integer(Types.DECIMAL)); + _typeSqlMap.put(byte[].class, new Integer(Types.VARBINARY)); + _typeSqlMap.put(java.sql.Timestamp.class, new Integer(Types.TIMESTAMP)); + _typeSqlMap.put(java.sql.Time.class, new Integer(Types.TIME)); + _typeSqlMap.put(java.sql.Date.class, new Integer(Types.DATE)); + _typeSqlMap.put(java.sql.Ref.class, new Integer(Types.REF)); + _typeSqlMap.put(Blob.class, new Integer(Types.BLOB)); + _typeSqlMap.put(Clob.class, new Integer(Types.CLOB)); + _typeSqlMap.put(java.sql.Array.class, new Integer(Types.ARRAY)); + _typeSqlMap.put(java.sql.Struct.class, new Integer(Types.STRUCT)); + _typeSqlMap.put(java.util.Date.class, new Integer(Types.TIMESTAMP)); + _typeSqlMap.put(java.util.Calendar.class, new Integer(Types.TIMESTAMP)); + _typeSqlMap.put(java.util.GregorianCalendar.class, new Integer(Types.TIMESTAMP)); + if (XMLBEANS_STRING_ENUM_ABSTRACT_BASE != null) { + _typeSqlMap.put(XMLBEANS_STRING_ENUM_ABSTRACT_BASE, new Integer(Types.VARCHAR)); + } + + // String to java.sql.Types + _typeSqlNameMap = new HashMap(TYPE_MAX * 2); + _typeSqlNameMap.put("BIT", new Integer(Types.BIT)); + _typeSqlNameMap.put("TINYINT", new Integer(Types.TINYINT)); + _typeSqlNameMap.put("SMALLINT", new Integer(Types.SMALLINT)); + _typeSqlNameMap.put("INTEGER", new Integer(Types.INTEGER)); + _typeSqlNameMap.put("BIGINT", new Integer(Types.BIGINT)); + _typeSqlNameMap.put("FLOAT", new Integer(Types.REAL)); + _typeSqlNameMap.put("REAL", new Integer(Types.REAL)); + _typeSqlNameMap.put("DOUBLE", new Integer(Types.DOUBLE)); + _typeSqlNameMap.put("NUMERIC", new Integer(Types.NUMERIC)); + _typeSqlNameMap.put("DECIMAL", new Integer(Types.DECIMAL)); + _typeSqlNameMap.put("CHAR", new Integer(Types.CHAR)); + _typeSqlNameMap.put("VARCHAR", new Integer(Types.VARCHAR)); + _typeSqlNameMap.put("LONGVARCHAR", new Integer(Types.LONGVARCHAR)); + _typeSqlNameMap.put("DATE", new Integer(Types.DATE)); + _typeSqlNameMap.put("TIME", new Integer(Types.TIME)); + _typeSqlNameMap.put("TIMESTAMP", new Integer(Types.TIMESTAMP)); + _typeSqlNameMap.put("BINARY", new Integer(Types.BINARY)); + _typeSqlNameMap.put("VARBINARY", new Integer(Types.VARBINARY)); + _typeSqlNameMap.put("LONGVARBINARY", new Integer(Types.LONGVARBINARY)); + _typeSqlNameMap.put("NULL", new Integer(Types.NULL)); + _typeSqlNameMap.put("OTHER", new Integer(Types.OTHER)); + _typeSqlNameMap.put("JAVA_OBJECT", new Integer(Types.JAVA_OBJECT)); + _typeSqlNameMap.put("DISTINCT", new Integer(Types.DISTINCT)); + _typeSqlNameMap.put("STRUCT", new Integer(Types.STRUCT)); + _typeSqlNameMap.put("ARRAY", new Integer(Types.ARRAY)); + _typeSqlNameMap.put("BLOB", new Integer(Types.BLOB)); + _typeSqlNameMap.put("CLOB", new Integer(Types.CLOB)); + _typeSqlNameMap.put("REF", new Integer(Types.REF)); + _typeSqlNameMap.put("DATALINK", new Integer(Types.DATALINK)); + _typeSqlNameMap.put("BOOLEAN", new Integer(Types.BOOLEAN)); + + // some JAVA synonyms + _typeSqlNameMap.put("BYTE", new Integer(Types.TINYINT)); + _typeSqlNameMap.put("SHORT", new Integer(Types.SMALLINT)); + _typeSqlNameMap.put("INT", new Integer(Types.INTEGER)); + _typeSqlNameMap.put("LONG", new Integer(Types.BIGINT)); + + // cache the Map.get method for efficiency + try { + _methodMapGet = Map.class.getMethod("get", new Class[]{Object.class}); + } catch (NoSuchMethodException e) { + throw new MapperException("Can not find java.util.Map.get(Object) method"); + } + } + + /** + * Convert a type string to its SQL Type int value. + * @param type A String containing the SQL type name. + * @return The SQL type, TYPE_UNKNOWN if cannot convert. + */ + public int convertStringToSQLType(String type) { + if (_typeSqlNameMap.containsKey(type.toUpperCase())) { + return _typeSqlNameMap.get(type.toUpperCase()); + } + return TYPE_UNKNOWN; + } + + /** + * Get the SQL type of a class, start at top level class an check all super classes until match is found. + * @param classType Class to get SQL type of. + * @return Types.OTHER if cannot find SQL type. + */ + public int getSqlType(Class classType) { + + final Class origType = classType; + while (classType != null) { + Integer type = _typeSqlMap.get(classType); + if (type != null) { + return type.intValue(); + } + classType = classType.getSuperclass(); + } + + // + // special check for blobs/clobs they are interfaces not derived from + // + if (Blob.class.isAssignableFrom(origType)) { + return _typeSqlMap.get(Blob.class).intValue(); + } else if (Clob.class.isAssignableFrom(origType)) { + return _typeSqlMap.get(Clob.class).intValue(); + } + + return Types.OTHER; + } + + /** + * Get the SQL type for an object. + * @param o Object to get SQL type of. + * @return SQL type of the object, Types.OTHER if cannot classify. + */ + public int getSqlType(Object o) { + if (null == o) { + return Types.NULL; + } + return getSqlType(o.getClass()); + } + + /** + * + * @param val + * @param args + * @return the type + * @throws IllegalAccessException + * @throws java.lang.reflect.InvocationTargetException + */ + public Object lookupType(Object val, Object[] args) + throws IllegalAccessException, InvocationTargetException + { + return _methodMapGet.invoke(val, args); + } + + /** + * Get the type id (defined by this class) for the given class. + * @param classType Class to get type of. + * @return Type id of class. + */ + public int getTypeId(Class classType) { + + final Class origType = classType; + while (null != classType) { + Integer typeObj = (Integer) _typeMap.get(classType); + if (null != typeObj) { + return typeObj.intValue(); + } + classType = classType.getSuperclass(); + } + + // + // special check for blobs/clobs they are interfaces not derived from + // + if (Blob.class.isAssignableFrom(origType)) { + return _typeMap.get(Blob.class).intValue(); + } else if (Clob.class.isAssignableFrom(origType)) { + return _typeMap.get(Clob.class).intValue(); + } + + return TYPE_UNKNOWN; + } + + /** + * Returns a primitive legal value as opposed to null if type is primitive. + * @param type type to get null value for. + * @return null value for specifed type. + */ + public Object fixNull(Class type) { + return type.isPrimitive() ? _primitiveDefaults.get(type) : null; + } + + /** + * Create an Object array for the given array. + * + * @param o An array. + * @return A new object array. + */ + public static Object[] toObjectArray(Object o) { + + Class clas = o.getClass().getComponentType(); + + if (null == clas) return null; + + Object[] arr; + + if (clas == Boolean.TYPE) { + boolean[] src = (boolean[]) o; + arr = new Object[src.length]; + for (int i = 0; i < src.length; i++) + arr[i] = new Boolean(src[i]); + } else if (clas == Character.TYPE) { + char[] src = (char[]) o; + arr = new Object[src.length]; + for (int i = 0; i < src.length; i++) + arr[i] = new Character(src[i]); + } else if (clas == Byte.TYPE) { + byte[] src = (byte[]) o; + arr = new Object[src.length]; + for (int i = 0; i < src.length; i++) + arr[i] = new Byte(src[i]); + } else if (clas == Short.TYPE) { + short[] src = (short[]) o; + arr = new Object[src.length]; + for (int i = 0; i < src.length; i++) + arr[i] = new Short(src[i]); + } else if (clas == Integer.TYPE) { + int[] src = (int[]) o; + arr = new Object[src.length]; + for (int i = 0; i < src.length; i++) + arr[i] = new Integer(src[i]); + } else if (clas == Long.TYPE) { + long[] src = (long[]) o; + arr = new Object[src.length]; + for (int i = 0; i < src.length; i++) + arr[i] = new Long(src[i]); + } else if (clas == Float.TYPE) { + float[] src = (float[]) o; + arr = new Object[src.length]; + for (int i = 0; i < src.length; i++) + arr[i] = new Float(src[i]); + } else if (clas == Double.TYPE) { + double[] src = (double[]) o; + arr = new Object[src.length]; + for (int i = 0; i < src.length; i++) + arr[i] = new Double(src[i]); + } else { + arr = (Object[]) o; + } + return arr; + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/UpdateableDTO.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/UpdateableDTO.java new file mode 100644 index 0000000..8053075 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/UpdateableDTO.java @@ -0,0 +1,242 @@ +package com.moparisthebest.jdbc; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import static com.moparisthebest.jdbc.TryClose.tryClose; + +public abstract class UpdateableDTO implements Finishable { + private boolean finished = false; + private Map changes = null; + + private String tableName; + private String whereClause; + + public static final String YES = "Y"; + public static final String NO = "N"; + + /** + * Will always return YES or NO from this class, so they CAN be compared with object equality '==' instead of '.equals()' + * @param bool + * @return + */ + public static String booleanToString(boolean bool){ + return bool ? YES : NO; + } + + public static boolean changed(Object oldValue, Object newValue){ + return oldValue != newValue && ((oldValue != null && !oldValue.equals(newValue)) || !newValue.equals(oldValue)); + } + + public static Object[] getUpdate(final StringBuilder sb, final Map changes, final String tableName, final String whereClause){ + if(changes.isEmpty()) + return new Object[0]; + sb.append("UPDATE ").append(tableName).append(" SET "); + int count = -1; + Object[] objectsToBind = new Object[changes.size()]; + boolean notFirst = false; + for (Map.Entry column : changes.entrySet()) { + if (column.getKey() == null) continue; + if (notFirst) sb.append(","); + else notFirst = true; + sb.append(column.getKey()).append("=?"); + objectsToBind[++count] = column.getValue(); + } + if(whereClause != null) + sb.append(" ").append(whereClause); + //System.out.println("updateRow: "+sb); + return objectsToBind; + } + + public static Object[] getInsert(final StringBuilder sb, final Map changes, final String tableName){ + if(changes.isEmpty()) + return new Object[0]; + sb.append("INSERT INTO ").append(tableName).append(" ("); + int count = -1; + Object[] objectsToBind = new Object[changes.size()]; + boolean notFirst = false; + for (Map.Entry column : changes.entrySet()) { + if (column.getKey() == null) continue; + if (notFirst) sb.append(","); + else notFirst = true; + sb.append(column.getKey()); + objectsToBind[++count] = column.getValue(); + } + sb.append(") VALUES (?"); + for(int x = 1; x < objectsToBind.length; ++x) + sb.append(",?"); + sb.append(")"); + //System.out.println("insertRow: "+sb); + return objectsToBind; + } + + public static int updateRow(final Connection conn, final Map changes, final String tableName, final String whereClause, final Object... additionalBindObjects) throws SQLException { + StringBuilder sb = new StringBuilder(); + Object[] objectsToBind = getUpdate(sb, changes, tableName, whereClause); + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sb.toString()); + QueryMapper.bindStatement(ps, objectsToBind, additionalBindObjects == null ? new Object[0] : additionalBindObjects); + return ps.executeUpdate(); + } finally { + tryClose(ps); + } + } + + public static int updateRow(final QueryMapper qm, final Map changes, final String tableName, final String whereClause, final Object... additionalBindObjects) throws SQLException { + StringBuilder sb = new StringBuilder(); + Object[] objectsToBind = getUpdate(sb, changes, tableName, whereClause); + return qm.executeUpdate(sb.toString(), objectsToBind, additionalBindObjects == null ? new Object[0] : additionalBindObjects); + } + + public static int insertRow(final Connection conn, final Map changes, final String tableName) throws SQLException { + StringBuilder sb = new StringBuilder(); + Object[] objectsToBind = getInsert(sb, changes, tableName); + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(sb.toString()); + QueryMapper.bindStatement(ps, objectsToBind); + return ps.executeUpdate(); + } finally { + tryClose(ps); + } + } + + public static int insertRow(final QueryMapper qm, final Map changes, final String tableName) throws SQLException { + StringBuilder sb = new StringBuilder(); + Object[] objectsToBind = getInsert(sb, changes, tableName); + return qm.executeUpdate(sb.toString(), objectsToBind); + } + + public static int deleteRow(final Connection conn, final String tableName, final String whereClause, final Object... additionalBindObjects) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(String.format("DELETE FROM %s %s", tableName, whereClause == null ? "" : whereClause)); + if(additionalBindObjects != null) + QueryMapper.bindStatement(ps, additionalBindObjects); + return ps.executeUpdate(); + } finally { + tryClose(ps); + } + } + + public static int deleteRow(final QueryMapper qm, final String tableName, final String whereClause, final Object... additionalBindObjects) throws SQLException { + return qm.executeUpdate(String.format("DELETE FROM %s %s", tableName, whereClause == null ? "" : whereClause), additionalBindObjects == null ? new Object[0] : additionalBindObjects); + } + + protected UpdateableDTO(String tableName, String whereClause) { + setTableName(tableName); + this.whereClause = whereClause; + } + + protected String getTableName() { + return tableName; + } + + protected void setTableName(String tableName) { + if(tableName == null) + throw new NullPointerException("tableName can never be null! Insert, update, or delete would never work!"); + this.tableName = tableName; + } + + protected String getWhereClause() { + return whereClause; + } + + protected void setWhereClause(String whereClause) { + this.whereClause = whereClause; + } + + protected final void change(String columnName, boolean oldValue, boolean newValue) { + change(columnName, booleanToString(oldValue), booleanToString(newValue)); + } + + /** + * Right now this is NOT thread-safe, if this class is to be used concurrently, it needs synchronized + * + * This never saves the ORIGINAL value, so if you change a certain column to something new, then change it back + * to the original, it is still counted as a change. The way to fix this would be another Map + * of ORIGINAL values, but I'm not sure this is desired. + * + * @param columnName + * @param newValue + */ + protected final boolean change(String columnName, Object oldValue, Object newValue) { + if(finished && changed(oldValue, newValue)){ + /* + System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++"); + System.out.println("before: "+this); + System.out.printf("changing column '%s' from '%s' to '%s'\n", columnName, oldValue, newValue); + new Exception("Stack trace").printStackTrace(System.out); + System.out.println("----------------------------------------------"); + */ + if (changes == null) + changes = new HashMap(); + changes.put(columnName, newValue); + return true; + } + return false; + } + + public boolean hasChanged() { + return changes != null && !changes.isEmpty(); // not null would probably suffice? + } + + public int updateRow(Connection conn) throws SQLException { + if (!hasChanged()) + return 0; + return updateRow(conn, changes, this.tableName, this.whereClause, this.getAdditionalBindObjects()); + } + + public int updateRow(QueryMapper qm) throws SQLException { + if (!hasChanged()) + return 0; + return updateRow(qm, changes, this.tableName, this.whereClause, this.getAdditionalBindObjects()); + } + + public int insertRow(Connection conn) throws SQLException { + if (!hasChanged()) + return 0; + return insertRow(conn, changes, this.tableName); + } + + public int insertRow(QueryMapper qm) throws SQLException { + if (!hasChanged()) + return 0; + return insertRow(qm, changes, this.tableName); + } + + public int deleteRow(Connection conn) throws SQLException { + return deleteRow(conn, this.tableName, this.whereClause, this.getAdditionalBindObjects()); + } + + public int deleteRow(QueryMapper qm) throws SQLException { + return deleteRow(qm, this.tableName, this.whereClause, this.getAdditionalBindObjects()); + } + + @Override + public void finish(ResultSet rs) throws SQLException { + this.finished = true; + } + + /** + * You will almost always want to return some objects here for your where clause + * + * if you need to bind a single null, return new Object[]{null}, otherwise nothing will be binded + * if you only need to bind a single non-null object, return that object here + * if you need to bind multiple objects, return an Object[] or a Collection + */ + public abstract Object getAdditionalBindObjects(); + + @Override + public String toString() { + return "UpdateableDTO{" + + "finished=" + finished + + ", changes=" + changes + + '}'; + } +} diff --git a/pom.xml b/pom.xml index f3d599a..1ffd326 100644 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,7 @@ beehive-jdbc-control beehive-ejb-control beehive-jms-control + beehive-jdbc-mapper @@ -227,6 +228,7 @@ 1.5 1.5 true +