Somewhat major re-factor, all tests pass now

This commit is contained in:
Travis Burtrum 2017-05-17 17:32:48 -04:00
parent b425bb49b0
commit e7639d740c
12 changed files with 132 additions and 78 deletions

View File

@ -132,7 +132,7 @@ public class CachingResultSetMapper extends ResultSetMapper {
} }
@Override @Override
protected <T> RowToObjectMapper<T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType) { protected <K, T> RowToObjectMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
return new CachingRowToObjectMapper<T>(cache, resultSet, returnTypeClass, cal, mapValType); return new CachingRowToObjectMapper<K, T>(cache, resultSet, returnTypeClass, cal, mapValType, mapKeyType);
} }
} }

View File

@ -11,18 +11,18 @@ import java.util.Map;
/** /**
* Maps same as RowToObjectMapper except caches constructor and field mappings * Maps same as RowToObjectMapper except caches constructor and field mappings
*/ */
public class CachingRowToObjectMapper<T> extends RowToObjectMapper<T> { public class CachingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
protected final Map<ResultSetKey, FieldMapping<T>> cache; protected final Map<ResultSetKey, FieldMapping<T>> cache;
protected final ResultSetKey keys; protected final ResultSetKey keys;
public CachingRowToObjectMapper(final Map<ResultSetKey, FieldMapping<?>> cache, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType) { public CachingRowToObjectMapper(final Map<ResultSetKey, FieldMapping<?>> cache, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
super(resultSet, returnTypeClass, cal, mapValType); super(resultSet, returnTypeClass, cal, mapValType, mapKeyType);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Map<ResultSetKey, FieldMapping<T>> genericCache = (Map<ResultSetKey, FieldMapping<T>>) (Object) cache; // ridiculous ain't it? final Map<ResultSetKey, FieldMapping<T>> genericCache = (Map<ResultSetKey, FieldMapping<T>>) (Object) cache; // ridiculous ain't it?
this.cache = genericCache; this.cache = genericCache;
try { try {
keys = new ResultSetKey(super.getKeysFromResultSet(), _returnTypeClass); keys = new ResultSetKey(super.getKeysFromResultSet(), _returnTypeClass, _mapKeyType);
//System.out.printf("keys: %s\n", keys); //System.out.printf("keys: %s\n", keys);
} catch (SQLException e) { } catch (SQLException e) {
throw new MapperException("CachingRowToObjectMapper: SQLException: " + e.getMessage(), e); throw new MapperException("CachingRowToObjectMapper: SQLException: " + e.getMessage(), e);
@ -55,11 +55,12 @@ public class CachingRowToObjectMapper<T> extends RowToObjectMapper<T> {
public static class ResultSetKey { public static class ResultSetKey {
protected final String[] keys; protected final String[] keys;
protected final Class<?> returnTypeClass; protected final Class<?> returnTypeClass, mapKeyType;
public ResultSetKey(final String[] keys, final Class<?> returnTypeClass) { public ResultSetKey(final String[] keys, final Class<?> returnTypeClass, final Class<?> mapKeyType) {
this.keys = keys; this.keys = keys;
this.returnTypeClass = returnTypeClass; this.returnTypeClass = returnTypeClass;
this.mapKeyType = mapKeyType;
} }
@Override @Override
@ -69,13 +70,18 @@ public class CachingRowToObjectMapper<T> extends RowToObjectMapper<T> {
final ResultSetKey that = (ResultSetKey) o; final ResultSetKey that = (ResultSetKey) o;
return Arrays.equals(keys, that.keys) && (returnTypeClass != null ? returnTypeClass.equals(that.returnTypeClass) : that.returnTypeClass == null); // Probably incorrect - comparing Object[] arrays with Arrays.equals
if (!Arrays.equals(keys, that.keys)) return false;
if (returnTypeClass != null ? !returnTypeClass.equals(that.returnTypeClass) : that.returnTypeClass != null)
return false;
return mapKeyType != null ? mapKeyType.equals(that.mapKeyType) : that.mapKeyType == null;
} }
@Override @Override
public int hashCode() { public int hashCode() {
int result = Arrays.hashCode(keys); int result = Arrays.hashCode(keys);
result = 31 * result + (returnTypeClass != null ? returnTypeClass.hashCode() : 0); result = 31 * result + (returnTypeClass != null ? returnTypeClass.hashCode() : 0);
result = 31 * result + (mapKeyType != null ? mapKeyType.hashCode() : 0);
return result; return result;
} }
@ -84,6 +90,7 @@ public class CachingRowToObjectMapper<T> extends RowToObjectMapper<T> {
return "ResultSetKey{" + return "ResultSetKey{" +
"keys=" + Arrays.toString(keys) + "keys=" + Arrays.toString(keys) +
", returnTypeClass=" + returnTypeClass + ", returnTypeClass=" + returnTypeClass +
", mapKeyType=" + mapKeyType +
'}'; '}';
} }
} }

View File

@ -15,7 +15,7 @@ public class CaseInsensitiveMapResultSetMapper extends ResultSetMapper {
} }
@Override @Override
protected <T> RowToObjectMapper<T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType) { protected <K, T> RowToObjectMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
return new CaseInsensitiveMapRowToObjectMapper<T>(resultSet, returnTypeClass, cal, mapValType); return new CaseInsensitiveMapRowToObjectMapper<K, T>(resultSet, returnTypeClass, cal, mapValType, mapKeyType);
} }
} }

View File

@ -8,7 +8,7 @@ import java.util.Map;
/** /**
* Created by mopar on 5/15/14. * Created by mopar on 5/15/14.
*/ */
public class CaseInsensitiveMapRowToObjectMapper<T> extends RowToObjectMapper<T> { public class CaseInsensitiveMapRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
public CaseInsensitiveMapRowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass) { public CaseInsensitiveMapRowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass) {
super(resultSet, returnTypeClass); super(resultSet, returnTypeClass);
} }
@ -25,6 +25,10 @@ public class CaseInsensitiveMapRowToObjectMapper<T> extends RowToObjectMapper<T>
super(resultSet, returnTypeClass, cal, mapValType); super(resultSet, returnTypeClass, cal, mapValType);
} }
public CaseInsensitiveMapRowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
super(resultSet, returnTypeClass, cal, mapValType, mapKeyType);
}
@Override @Override
protected Map<String, Object> getMapImplementation() throws IllegalAccessException, InstantiationException { protected Map<String, Object> getMapImplementation() throws IllegalAccessException, InstantiationException {
if(HashMap.class.equals(_returnTypeClass)) if(HashMap.class.equals(_returnTypeClass))

View File

@ -19,7 +19,7 @@ public class CleaningResultSetMapper<E> extends ResultSetMapper {
@Override @Override
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
protected <T> RowToObjectMapper<T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType) { protected <K, T> RowToObjectMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
return new CleaningRowToObjectMapper<T>((Cleaner<T>)cleaner, resultSet, returnTypeClass, cal, mapValType); return new CleaningRowToObjectMapper<K, T>((Cleaner<T>)cleaner, resultSet, returnTypeClass, cal, mapValType, mapKeyType);
} }
} }

View File

@ -1,21 +1,22 @@
package com.moparisthebest.jdbc; package com.moparisthebest.jdbc;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Calendar; import java.util.Calendar;
public class CleaningRowToObjectMapper<T> extends RowToObjectMapper<T> { public class CleaningRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
private final Cleaner<T> cleaner; private final Cleaner<T> cleaner;
public CleaningRowToObjectMapper(Cleaner<T> cleaner, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType) { public CleaningRowToObjectMapper(Cleaner<T> cleaner, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
super(resultSet, returnTypeClass, cal, mapValType); super(resultSet, returnTypeClass, cal, mapValType, mapKeyType);
if (cleaner == null) if (cleaner == null)
throw new NullPointerException("cleaner cannot be null!"); throw new NullPointerException("cleaner cannot be null!");
this.cleaner = cleaner; this.cleaner = cleaner;
} }
@Override @Override
public T mapRowToReturnType() { public T mapRowToReturnType() throws SQLException {
return cleaner.clean(super.mapRowToReturnType()); return cleaner.clean(super.mapRowToReturnType());
} }
} }

View File

@ -25,7 +25,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class CompilingResultSetMapper extends ResultSetMapper { public class CompilingResultSetMapper extends ResultSetMapper {
protected final Compiler compiler = new Compiler(); protected final Compiler compiler = new Compiler();
protected final Map<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?>> cache; protected final Map<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>> cache;
/** /**
* CompilingResultSetMapper with optional maxEntries, expiring old ones in LRU fashion * CompilingResultSetMapper with optional maxEntries, expiring old ones in LRU fashion
@ -40,14 +40,14 @@ public class CompilingResultSetMapper extends ResultSetMapper {
final float loadFactor = 0.75f; // default for HashMaps final float loadFactor = 0.75f; // default for HashMaps
// if we set the initialCapacity this way, nothing should ever need re-sized // if we set the initialCapacity this way, nothing should ever need re-sized
final int initialCapacity = ((int) Math.ceil(maxEntries / loadFactor)) + 1; final int initialCapacity = ((int) Math.ceil(maxEntries / loadFactor)) + 1;
cache = new LinkedHashMap<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?>>(initialCapacity, loadFactor, true) { cache = new LinkedHashMap<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>>(initialCapacity, loadFactor, true) {
@Override @Override
protected boolean removeEldestEntry(final Map.Entry<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?>> eldest) { protected boolean removeEldestEntry(final Map.Entry<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>> eldest) {
return size() > maxEntries; return size() > maxEntries;
} }
}; };
} else } else
cache = new HashMap<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?>>(); cache = new HashMap<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>>();
} }
/** /**
@ -83,7 +83,7 @@ public class CompilingResultSetMapper extends ResultSetMapper {
* @param arrayMaxLength max array/list/map length, a value of less than 1 indicates that all rows from the ResultSet should be included * @param arrayMaxLength max array/list/map length, a value of less than 1 indicates that all rows from the ResultSet should be included
* @param cache any Map implementation for cache you wish, does not need to handle null keys or values * @param cache any Map implementation for cache you wish, does not need to handle null keys or values
*/ */
public CompilingResultSetMapper(final Calendar cal, final int arrayMaxLength, final Map<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?>> cache) { public CompilingResultSetMapper(final Calendar cal, final int arrayMaxLength, final Map<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>> cache) {
super(cal, arrayMaxLength); super(cal, arrayMaxLength);
if (cache == null) if (cache == null)
throw new IllegalArgumentException("cache cannot be null"); throw new IllegalArgumentException("cache cannot be null");
@ -96,7 +96,7 @@ public class CompilingResultSetMapper extends ResultSetMapper {
* @param arrayMaxLength max array/list/map length, a value of less than 1 indicates that all rows from the ResultSet should be included * @param arrayMaxLength max array/list/map length, a value of less than 1 indicates that all rows from the ResultSet should be included
* @param cache any Map implementation for cache you wish, does not need to handle null keys or values * @param cache any Map implementation for cache you wish, does not need to handle null keys or values
*/ */
public CompilingResultSetMapper(final int arrayMaxLength, final Map<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?>> cache) { public CompilingResultSetMapper(final int arrayMaxLength, final Map<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>> cache) {
this(null, arrayMaxLength, cache); this(null, arrayMaxLength, cache);
} }
@ -105,7 +105,7 @@ public class CompilingResultSetMapper extends ResultSetMapper {
* *
* @param cache any Map implementation for cache you wish, does not need to handle null keys or values * @param cache any Map implementation for cache you wish, does not need to handle null keys or values
*/ */
public CompilingResultSetMapper(final Map<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?>> cache) { public CompilingResultSetMapper(final Map<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>> cache) {
this(-1, cache); this(-1, cache);
} }
@ -118,9 +118,9 @@ public class CompilingResultSetMapper extends ResultSetMapper {
*/ */
public CompilingResultSetMapper(final Calendar cal, final int arrayMaxLength, final boolean threadSafe) { public CompilingResultSetMapper(final Calendar cal, final int arrayMaxLength, final boolean threadSafe) {
this(cal, arrayMaxLength, threadSafe ? this(cal, arrayMaxLength, threadSafe ?
new ConcurrentHashMap<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?>>() new ConcurrentHashMap<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>>()
: :
new HashMap<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?>>() new HashMap<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>>()
); );
} }
@ -144,7 +144,7 @@ public class CompilingResultSetMapper extends ResultSetMapper {
} }
@Override @Override
protected <T> RowToObjectMapper<T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType) { protected <K, T> RowToObjectMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
return new CompilingRowToObjectMapper<T>(compiler, cache, resultSet, returnTypeClass, cal, mapValType); return new CompilingRowToObjectMapper<K, T>(compiler, cache, resultSet, returnTypeClass, cal, mapValType, mapKeyType);
} }
} }

View File

@ -26,20 +26,23 @@ import java.util.Map;
* 1. Normally a subclass of RowToObjectMapper can overload the getMapImplementation() method to change some behavior, * 1. Normally a subclass of RowToObjectMapper can overload the getMapImplementation() method to change some behavior,
* @see CaseInsensitiveMapRowToObjectMapper , but that method is never called with this implementation. * @see CaseInsensitiveMapRowToObjectMapper , but that method is never called with this implementation.
*/ */
public class CompilingRowToObjectMapper<T> extends RowToObjectMapper<T> { public class CompilingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
public static final String firstColumnError = "Cannot call getFirstColumn when mapKeyType is null!";
protected final Compiler compiler; protected final Compiler compiler;
protected final ResultSetToObject<T> resultSetToObject; protected final ResultSetToObject<K, T> resultSetToObject;
protected String[] keys = null; // for caching if we must generate class protected String[] keys = null; // for caching if we must generate class
public CompilingRowToObjectMapper(final Compiler compiler, final Map<CachingRowToObjectMapper.ResultSetKey, ResultSetToObject<?>> cache, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType) { public CompilingRowToObjectMapper(final Compiler compiler, final Map<CachingRowToObjectMapper.ResultSetKey, ResultSetToObject<?,?>> cache, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
super(resultSet, returnTypeClass, cal, mapValType); super(resultSet, returnTypeClass, cal, mapValType, mapKeyType);
this.compiler = compiler; this.compiler = compiler;
try { try {
final CachingRowToObjectMapper.ResultSetKey keys = new CachingRowToObjectMapper.ResultSetKey(super.getKeysFromResultSet(), _returnTypeClass); final CachingRowToObjectMapper.ResultSetKey keys = new CachingRowToObjectMapper.ResultSetKey(super.getKeysFromResultSet(), _returnTypeClass, _mapKeyType);
//System.out.printf("keys: %s\n", keys); //System.out.printf("keys: %s\n", keys);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final ResultSetToObject<T> resultSetToObject = (ResultSetToObject<T>) cache.get(keys); final ResultSetToObject<K,T> resultSetToObject = (ResultSetToObject<K,T>) cache.get(keys);
if (resultSetToObject == null) { if (resultSetToObject == null) {
//System.out.printf("cache miss, keys: %s\n", keys); //System.out.printf("cache miss, keys: %s\n", keys);
// generate and put into cache // generate and put into cache
@ -60,15 +63,15 @@ public class CompilingRowToObjectMapper<T> extends RowToObjectMapper<T> {
} }
@Override @Override
public T mapRowToReturnType() { public T mapRowToReturnType() throws SQLException {
try { return resultSetToObject.toObject(_resultSet, _cal);
return resultSetToObject.toObject(_resultSet, _cal);
} catch (SQLException e) {
throw new MapperException(e.getMessage(), e);
}
} }
// todo: generate/cache these to make it faster for Map and MapCollection? maybe just getKey and getVal methods instead @Override
public K getMapKey() throws SQLException {
return resultSetToObject.getFirstColumn(_resultSet, _cal);
}
// todo: generate/cache these to make it faster for Map and MapCollection? maybe just getKey and getVal methods instead
/* /*
@Override @Override
public <E> E extractColumnValue(final int index, final Class<E> classType) throws SQLException { public <E> E extractColumnValue(final int index, final Class<E> classType) throws SQLException {
@ -88,11 +91,14 @@ public class CompilingRowToObjectMapper<T> extends RowToObjectMapper<T> {
throw new MapperException("not supported here"); throw new MapperException("not supported here");
} }
public interface ResultSetToObject<T> { public interface ResultSetToObject<K, T> {
K getFirstColumn(final ResultSet rs, final Calendar cal) throws SQLException;
T toObject(final ResultSet rs, final Calendar cal) throws SQLException; T toObject(final ResultSet rs, final Calendar cal) throws SQLException;
} }
protected String typeFromName(final Class<?> type) { protected String typeFromName(final Class<?> type) {
if(type == null)
return "Object";
if(_columnCount == 1 && type.isPrimitive()) { if(_columnCount == 1 && type.isPrimitive()) {
// need the object equivalent here, what is the best way? this works, isn't pretty... // need the object equivalent here, what is the best way? this works, isn't pretty...
if(type.equals(Character.TYPE)) if(type.equals(Character.TYPE))
@ -141,27 +147,44 @@ public class CompilingRowToObjectMapper<T> extends RowToObjectMapper<T> {
} }
// code generation down here // code generation down here
protected ResultSetToObject<T> genClass() { protected ResultSetToObject<K, T> genClass() {
final String className = "CompilingMapper"; final String className = "CompilingMapper";
final String tType = typeFromName(_returnTypeClass); final String tType = typeFromName(_returnTypeClass);
final String kType = typeFromName(_mapKeyType);
final String header = final String header =
"import static com.moparisthebest.jdbc.util.ResultSetUtil.*;\n\n" + "import static com.moparisthebest.jdbc.util.ResultSetUtil.*;\n\n" +
"public final class " + className + "public final class " + className +
" implements com.moparisthebest.jdbc.CompilingRowToObjectMapper.ResultSetToObject<" + tType + "> {\n" + " implements com.moparisthebest.jdbc.CompilingRowToObjectMapper.ResultSetToObject<"+ kType +"," + tType + "> {\n" +
" public " + tType + " toObject(final java.sql.ResultSet rs, final java.util.Calendar cal) throws java.sql.SQLException {\n"; " public " + tType + " toObject(final java.sql.ResultSet rs, final java.util.Calendar cal) throws java.sql.SQLException {\n";
final String footer = " }\n" + final String footer = ";\n }\n" +
"}\n"; "}\n";
final StringBuilder java = new StringBuilder(header); final StringBuilder java = new StringBuilder(header);
//java.append("return null;\n"); //java.append("return null;\n");
gen(java, tType); gen(java, tType);
java.append(" }\n\n public ").append(kType).append(" getFirstColumn(final java.sql.ResultSet rs, final java.util.Calendar cal) throws java.sql.SQLException {\n ");
if(_mapKeyType != null){
java.append("return ");
extractColumnValueString(java, 1, _tmf.getTypeId(_mapKeyType));
} else {
java.append("throw new com.moparisthebest.jdbc.MapperException(com.moparisthebest.jdbc.CompilingRowToObjectMapper.firstColumnError)");
}
java.append(footer); java.append(footer);
//System.out.println(java); System.out.println(java);
return compiler.compile(className, java); return compiler.compile(className, java);
} }
protected void gen(final StringBuilder java, final String tType) { protected void gen(final StringBuilder java, final String tType) {
if(mapOnlySecondColumn){
java.append("return ");
extractColumnValueString(java, 2, _tmf.getTypeId(_returnTypeClass));
java.append(";\n");
return;
}
lazyLoadConstructor(); lazyLoadConstructor();
if (resultSetConstructor) { if (resultSetConstructor) {

View File

@ -120,7 +120,7 @@ public class ResultSetMapper {
T ret = null; T ret = null;
try { try {
if (rs.next()) if (rs.next())
ret = getRowMapper(rs, componentType, cal, mapValType).mapRowToReturnType(); ret = getRowMapper(rs, componentType, cal, mapValType, null).mapRowToReturnType();
} catch (SQLException e) { } catch (SQLException e) {
// ignore // ignore
} }
@ -151,7 +151,7 @@ public class ResultSetMapper {
// a value of less than 1 indicates that all rows from the ResultSet should be included. // a value of less than 1 indicates that all rows from the ResultSet should be included.
final boolean unlimitedRows = arrayMaxLength < 1; final boolean unlimitedRows = arrayMaxLength < 1;
final RowToObjectMapper<E> rowMapper = getRowMapper(rs, componentType, cal, mapValType); final RowToObjectMapper<?, E> rowMapper = getRowMapper(rs, componentType, cal, mapValType, null);
for (; (unlimitedRows || numRows != arrayMaxLength) && rs.next(); ++numRows) { for (; (unlimitedRows || numRows != arrayMaxLength) && rs.next(); ++numRows) {
E object = rowMapper.mapRowToReturnType(); E object = rowMapper.mapRowToReturnType();
@ -230,12 +230,11 @@ public class ResultSetMapper {
// a value of less than 1 indicates that all rows from the ResultSet should be included. // a value of less than 1 indicates that all rows from the ResultSet should be included.
final boolean unlimitedRows = arrayMaxLength < 1; final boolean unlimitedRows = arrayMaxLength < 1;
final RowToObjectMapper<E> rowMapper = getRowMapper(rs, componentType, cal, mapValType); final RowToObjectMapper<K, E> rowMapper = getRowMapper(rs, componentType, cal, mapValType, mapKeyType);
boolean onlyTwoColumns = rowMapper.getColumnCount() == 2;
for (; (unlimitedRows || numRows != arrayMaxLength) && rs.next(); ++numRows) { for (; (unlimitedRows || numRows != arrayMaxLength) && rs.next(); ++numRows) {
K key = rowMapper.extractColumnValue(1, mapKeyType); K key = rowMapper.getMapKey();
E value = onlyTwoColumns ? rowMapper.extractColumnValue(2, componentType) : rowMapper.mapRowToReturnType(); E value = rowMapper.mapRowToReturnType();
map = softMap.get(); map = softMap.get();
if (map == null) if (map == null)
@ -312,12 +311,11 @@ public class ResultSetMapper {
// a value of less than 1 indicates that all rows from the ResultSet should be included. // a value of less than 1 indicates that all rows from the ResultSet should be included.
final boolean unlimitedRows = arrayMaxLength < 1; final boolean unlimitedRows = arrayMaxLength < 1;
final RowToObjectMapper<C> rowMapper = getRowMapper(rs, componentType, cal, mapValType); final RowToObjectMapper<K, C> rowMapper = getRowMapper(rs, componentType, cal, mapValType, mapKeyType);
boolean onlyTwoColumns = rowMapper.getColumnCount() == 2;
for (; (unlimitedRows || numRows != arrayMaxLength) && rs.next(); ++numRows) { for (; (unlimitedRows || numRows != arrayMaxLength) && rs.next(); ++numRows) {
K key = rowMapper.extractColumnValue(1, mapKeyType); K key = rowMapper.getMapKey();
C value = onlyTwoColumns ? rowMapper.extractColumnValue(2, componentType) : rowMapper.mapRowToReturnType(); C value = rowMapper.mapRowToReturnType();
map = softMap.get(); map = softMap.get();
if (map == null) if (map == null)
@ -406,8 +404,8 @@ public class ResultSetMapper {
} }
// fairly un-interesting methods below here // fairly un-interesting methods below here
protected <T> RowToObjectMapper<T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType) { protected <K, T> RowToObjectMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
return new RowToObjectMapper<T>(resultSet, returnTypeClass, cal, mapValType); return new RowToObjectMapper<K, T>(resultSet, returnTypeClass, cal, mapValType, mapKeyType);
} }
protected void warnOnMaxLength(final int numRows, final int arrayMaxLength, final ResultSet rs) { protected void warnOnMaxLength(final int numRows, final int arrayMaxLength, final ResultSet rs) {

View File

@ -37,7 +37,7 @@ import java.util.regex.Pattern;
* RowMapperFactory. * RowMapperFactory.
* *
*/ */
public abstract class RowMapper { public abstract class RowMapper<K, T> {
private static final String SETTER_NAME_REGEX = "^(set)([A-Z_]\\w*+)"; private static final String SETTER_NAME_REGEX = "^(set)([A-Z_]\\w*+)";
protected static final TypeMappingsFactory _tmf = TypeMappingsFactory.getInstance(); protected static final TypeMappingsFactory _tmf = TypeMappingsFactory.getInstance();
@ -50,34 +50,51 @@ public abstract class RowMapper {
protected final Calendar _cal; protected final Calendar _cal;
/** Class to map ResultSet Rows to. */ /** Class to map ResultSet Rows to. */
protected final Class<?> _returnTypeClass; protected final Class<T> _returnTypeClass;
protected final Class<K> _mapKeyType;
protected final int _columnCount; protected final int _columnCount;
protected final boolean mapOnlySecondColumn;
/** /**
* Create a new RowMapper for the specified ResultSet and return type Class. * Create a new RowMapper for the specified ResultSet and return type Class.
* @param resultSet ResultSet to map * @param resultSet ResultSet to map
* @param returnTypeClass Class to map ResultSet rows to. * @param returnTypeClass Class to map ResultSet rows to.
* @param cal Calendar instance for date/time values. * @param cal Calendar instance for date/time values.
*/ */
protected RowMapper(ResultSet resultSet, Class<?> returnTypeClass, Calendar cal) { protected RowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<K> mapKeyType) {
_resultSet = resultSet; _resultSet = resultSet;
_returnTypeClass = returnTypeClass; _returnTypeClass = returnTypeClass;
_cal = cal; _cal = cal;
_mapKeyType = mapKeyType;
try { try {
_columnCount = resultSet.getMetaData().getColumnCount(); _columnCount = resultSet.getMetaData().getColumnCount();
} catch (SQLException e) { } catch (SQLException e) {
throw new MapperException("RowToObjectMapper: SQLException: " + e.getMessage(), e); throw new MapperException("RowToObjectMapper: SQLException: " + e.getMessage(), e);
} }
mapOnlySecondColumn = _mapKeyType != null && _columnCount == 2;
}
protected RowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal) {
this(resultSet, returnTypeClass, cal, null);
} }
/** /**
* Map a ResultSet row to the return type class * Map a ResultSet row to the return type class
* @return An instance of class. * @return An instance of class, if _mapKeyType is not null and _columnCount is 2, return only index 2
*/ */
public abstract Object mapRowToReturnType(); public abstract T mapRowToReturnType() throws SQLException;
/**
* key for map
* @return index number 1, with type of _mapKeyType
* @throws MapperException if _mapKeyType is null
*/
public abstract K getMapKey() throws SQLException;
/** /**
* Build a String array of column names from the ResultSet. * Build a String array of column names from the ResultSet.

View File

@ -47,7 +47,7 @@ import static com.moparisthebest.jdbc.UpdateableDTO.NO;
* *
* @author Travis Burtrum (modifications from beehive) * @author Travis Burtrum (modifications from beehive)
*/ */
public class RowToObjectMapper<T> extends RowMapper { public class RowToObjectMapper<K, T> extends RowMapper<K, T> {
public static final int TYPE_BOOLEAN = _tmf.getTypeId(Boolean.TYPE);//TypeMappingsFactory.TYPE_BOOLEAN; // not public? 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? public static final int TYPE_BOOLEAN_OBJ = _tmf.getTypeId(Boolean.class);//TypeMappingsFactory.TYPE_BOOLEAN_OBJ; // not public?
@ -77,6 +77,11 @@ public class RowToObjectMapper<T> extends RowMapper {
this(resultSet, returnTypeClass, cal, null); this(resultSet, returnTypeClass, cal, null);
} }
public RowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType) {
this(resultSet, returnTypeClass, cal, mapValType, null);
}
/** /**
* Create a new RowToObjectMapper. * Create a new RowToObjectMapper.
* *
@ -84,8 +89,8 @@ public class RowToObjectMapper<T> extends RowMapper {
* @param returnTypeClass Class to map to. * @param returnTypeClass Class to map to.
* @param cal Calendar instance for date/time mappings. * @param cal Calendar instance for date/time mappings.
*/ */
public RowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType) { public RowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
super(resultSet, returnTypeClass, cal); super(resultSet, returnTypeClass, cal, mapKeyType);
returnMap = Map.class.isAssignableFrom(returnTypeClass); returnMap = Map.class.isAssignableFrom(returnTypeClass);
if(returnMap){ if(returnMap){
_returnTypeClass = ResultSetMapper.getConcreteClass(returnTypeClass, HashMap.class); _returnTypeClass = ResultSetMapper.getConcreteClass(returnTypeClass, HashMap.class);
@ -136,13 +141,21 @@ public class RowToObjectMapper<T> extends RowMapper {
return (Map<String, Object>)_returnTypeClass.newInstance(); return (Map<String, Object>)_returnTypeClass.newInstance();
} }
@Override
public K getMapKey() throws SQLException {
return this.extractColumnValue(1, _mapKeyType);
}
/** /**
* Do the mapping. * Do the mapping.
* *
* @return An object instance. * @return An object instance.
*/ */
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
public T mapRowToReturnType() { public T mapRowToReturnType() throws SQLException {
if(mapOnlySecondColumn)
return this.extractColumnValue(2, _returnTypeClass);
lazyLoadConstructor(); lazyLoadConstructor();
@ -300,15 +313,6 @@ public class RowToObjectMapper<T> extends RowMapper {
return classType.cast(extractColumnValue(index, _tmf.getTypeId(classType))); 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 * Build the structures necessary to do the mapping
* *

View File

@ -9,7 +9,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/ */
public class StaticCompilingResultSetMapper extends CompilingResultSetMapper { public class StaticCompilingResultSetMapper extends CompilingResultSetMapper {
private static final Map<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?>> cache = new ConcurrentHashMap<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?>>(); private static final Map<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>> cache = new ConcurrentHashMap<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>>();
public static final StaticCompilingResultSetMapper instance = new StaticCompilingResultSetMapper(); public static final StaticCompilingResultSetMapper instance = new StaticCompilingResultSetMapper();