Allow CompilingRowToObjectMapper to use reflection for non-public fields optionally

This commit is contained in:
Travis Burtrum 2017-06-18 21:58:55 -04:00
parent f9ac10c70f
commit 115a62293e
10 changed files with 185 additions and 186 deletions

View File

@ -0,0 +1,26 @@
package com.moparisthebest.jdbc.util;
import java.lang.reflect.Field;
/**
* Created by mopar on 6/19/17.
*/
public class ReflectionUtil {
public static Field getAccessibleField(final Class<?> clazz, final String declaredField) {
try {
final Field ret = clazz.getDeclaredField(declaredField);
ret.setAccessible(true);
return ret;
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
public static void setValue(final Field field, final Object obj, final Object value) {
try {
field.set(obj, value);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -1,5 +1,7 @@
package com.moparisthebest.jdbc; package com.moparisthebest.jdbc;
import com.moparisthebest.jdbc.util.CacheUtil;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.util.Calendar; import java.util.Calendar;
import java.util.HashMap; import java.util.HashMap;
@ -24,18 +26,7 @@ public class CachingResultSetMapper extends ResultSetMapper {
*/ */
public CachingResultSetMapper(final Calendar cal, final int arrayMaxLength, final int maxEntries) { public CachingResultSetMapper(final Calendar cal, final int arrayMaxLength, final int maxEntries) {
super(cal, arrayMaxLength); super(cal, arrayMaxLength);
if (maxEntries > 0) { // we want a limited cache cache = CacheUtil.getCache(maxEntries);
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<CachingRowToObjectMapper.ResultSetKey, CachingRowToObjectMapper.FieldMapping<?>>(initialCapacity, loadFactor, true) {
@Override
protected boolean removeEldestEntry(final Map.Entry<CachingRowToObjectMapper.ResultSetKey, CachingRowToObjectMapper.FieldMapping<?>> eldest) {
return size() > maxEntries;
}
};
} else
cache = new HashMap<CachingRowToObjectMapper.ResultSetKey, CachingRowToObjectMapper.FieldMapping<?>>();
} }
/** /**
@ -105,11 +96,7 @@ public class CachingResultSetMapper extends ResultSetMapper {
* @param threadSafe true uses a thread-safe cache implementation (currently ConcurrentHashMap), false uses regular HashMap * @param threadSafe true uses a thread-safe cache implementation (currently ConcurrentHashMap), false uses regular HashMap
*/ */
public CachingResultSetMapper(final Calendar cal, final int arrayMaxLength, final boolean threadSafe) { public CachingResultSetMapper(final Calendar cal, final int arrayMaxLength, final boolean threadSafe) {
this(cal, arrayMaxLength, threadSafe ? this(cal, arrayMaxLength, CacheUtil.<CachingRowToObjectMapper.ResultSetKey, CachingRowToObjectMapper.FieldMapping<?>>getCache(threadSafe));
new ConcurrentHashMap<CachingRowToObjectMapper.ResultSetKey, CachingRowToObjectMapper.FieldMapping<?>>()
:
new HashMap<CachingRowToObjectMapper.ResultSetKey, CachingRowToObjectMapper.FieldMapping<?>>()
);
} }
/** /**

View File

@ -11,52 +11,22 @@ public class CleaningCompilingResultSetMapper<E> extends CompilingResultSetMappe
private final Cleaner<E> cleaner; private final Cleaner<E> cleaner;
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final Calendar cal, final int arrayMaxLength, final int maxEntries) { public CleaningCompilingResultSetMapper(final Calendar cal, final int arrayMaxLength, final CompilingRowToObjectMapper.Cache cache, final Cleaner<E> cleaner) {
super(cal, arrayMaxLength, maxEntries);
this.cleaner = cleaner;
}
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final Calendar cal, final int arrayMaxLength) {
super(cal, arrayMaxLength);
this.cleaner = cleaner;
}
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final int arrayMaxLength) {
super(arrayMaxLength);
this.cleaner = cleaner;
}
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner) {
this.cleaner = cleaner;
}
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final Calendar cal, final int arrayMaxLength, final Map<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?, ?>> cache) {
super(cal, arrayMaxLength, cache); super(cal, arrayMaxLength, cache);
this.cleaner = cleaner; this.cleaner = cleaner;
} }
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final int arrayMaxLength, final Map<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?, ?>> cache) { public CleaningCompilingResultSetMapper(final CompilingRowToObjectMapper.Cache cache, final Cleaner<E> cleaner) {
super(arrayMaxLength, cache);
this.cleaner = cleaner;
}
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final Map<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?, ?>> cache) {
super(cache); super(cache);
this.cleaner = cleaner; this.cleaner = cleaner;
} }
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final Calendar cal, final int arrayMaxLength, final boolean threadSafe) { public CleaningCompilingResultSetMapper(final int arrayMaxLength, final CompilingRowToObjectMapper.Cache cache, final Cleaner<E> cleaner) {
super(cal, arrayMaxLength, threadSafe); super(arrayMaxLength, cache);
this.cleaner = cleaner; this.cleaner = cleaner;
} }
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final int arrayMaxLength, final boolean threadSafe) { public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner) {
super(arrayMaxLength, threadSafe);
this.cleaner = cleaner;
}
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final boolean threadSafe) {
super(threadSafe);
this.cleaner = cleaner; this.cleaner = cleaner;
} }

View File

@ -4,10 +4,6 @@ import com.moparisthebest.classgen.Compiler;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.util.Calendar; import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* This generally follows the contract of ResultSetMapper, with the differences specified in CompilingRowToObjectMapper. * This generally follows the contract of ResultSetMapper, with the differences specified in CompilingRowToObjectMapper.
@ -15,132 +11,33 @@ import java.util.concurrent.ConcurrentHashMap;
* This does cache compiled code based on column name/order and DTO being mapped to, so the (rather heavy) * This does cache compiled code based on column name/order and DTO being mapped to, so the (rather heavy)
* code generation/compilation/instantiation only happens once for each query/dto, and is very fast on subsequent calls. * code generation/compilation/instantiation only happens once for each query/dto, and is very fast on subsequent calls.
* <p> * <p>
* By default this uses a plain HashMap for the cache, unbounded, and not thread-safe. There are overloaded constructors * By default this uses a plain HashMap for the cache, unbounded, and not thread-safe. Use CacheUtil to get Maps for your
* you can use to tell it to be threadSafe in which case it uses an unbounded ConcurrentHashMap, or to give it a maxEntries * preferred use case. You cansend in your own custom Map implementation, CompilingResultSetMapper guarantees null will
* in which case it uses a LinkedHashMap and clears out old entries in LRU order keeping only maxEntries. Lastly you can * never be used for key or value.
* send in your own custom Map implementation, CompilingResultSetMapper guarantees null will never be used for key or value.
* *
* @see CompilingRowToObjectMapper * @see CompilingRowToObjectMapper
*/ */
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<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>> cache; protected final CompilingRowToObjectMapper.Cache cache;
/** public CompilingResultSetMapper(final Calendar cal, final int arrayMaxLength, final CompilingRowToObjectMapper.Cache cache) {
* CompilingResultSetMapper with optional maxEntries, expiring old ones in LRU fashion
*
* @param cal optional calendar for date/time values
* @param arrayMaxLength max array/list/map length, a value of less than 1 indicates that all rows from the ResultSet should be included
* @param maxEntries max cached compiled entries to keep in cache, < 1 means unlimited
*/
public CompilingResultSetMapper(final Calendar cal, final int arrayMaxLength, final int maxEntries) {
super(cal, arrayMaxLength); super(cal, arrayMaxLength);
if (maxEntries > 0) { // we want a limited cache this.cache = cache == null ? new CompilingRowToObjectMapper.Cache() : 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<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>>(initialCapacity, loadFactor, true) {
@Override
protected boolean removeEldestEntry(final Map.Entry<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>> eldest) {
return size() > maxEntries;
}
};
} else
cache = new HashMap<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>>();
} }
/** public CompilingResultSetMapper(final CompilingRowToObjectMapper.Cache cache) {
* CompilingResultSetMapper with unlimited cache this.cache = cache == null ? new CompilingRowToObjectMapper.Cache() : cache;
*
* @param cal optional calendar for date/time values
* @param arrayMaxLength max array/list/map length, a value of less than 1 indicates that all rows from the ResultSet should be included
*/
public CompilingResultSetMapper(final Calendar cal, final int arrayMaxLength) {
this(cal, arrayMaxLength, 0); // default unlimited cache
} }
/** public CompilingResultSetMapper(final int arrayMaxLength, final CompilingRowToObjectMapper.Cache cache) {
* CompilingResultSetMapper with unlimited cache super(arrayMaxLength);
* this.cache = cache == null ? new CompilingRowToObjectMapper.Cache() : cache;
* @param arrayMaxLength max array/list/map length, a value of less than 1 indicates that all rows from the ResultSet should be included
*/
public CompilingResultSetMapper(final int arrayMaxLength) {
this(null, arrayMaxLength);
} }
/**
* CompilingResultSetMapper with unlimited cache
*/
public CompilingResultSetMapper() { public CompilingResultSetMapper() {
this(-1); this.cache = new CompilingRowToObjectMapper.Cache();
}
/**
* CompilingResultSetMapper with custom cache implementation
*
* @param cal optional calendar for date/time values
* @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
*/
public CompilingResultSetMapper(final Calendar cal, final int arrayMaxLength, final Map<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>> cache) {
super(cal, arrayMaxLength);
if (cache == null)
throw new IllegalArgumentException("cache cannot be null");
this.cache = cache;
}
/**
* CompilingResultSetMapper with custom cache implementation
*
* @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
*/
public CompilingResultSetMapper(final int arrayMaxLength, final Map<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>> cache) {
this(null, arrayMaxLength, cache);
}
/**
* CompilingResultSetMapper with custom cache implementation
*
* @param cache any Map implementation for cache you wish, does not need to handle null keys or values
*/
public CompilingResultSetMapper(final Map<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>> cache) {
this(-1, cache);
}
/**
* CompilingResultSetMapper with optionally threadSafe cache implementation
*
* @param cal optional calendar for date/time values
* @param arrayMaxLength max array/list/map length, a value of less than 1 indicates that all rows from the ResultSet should be included
* @param threadSafe true uses a thread-safe cache implementation (currently ConcurrentHashMap), false uses regular HashMap
*/
public CompilingResultSetMapper(final Calendar cal, final int arrayMaxLength, final boolean threadSafe) {
this(cal, arrayMaxLength, threadSafe ?
new ConcurrentHashMap<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>>()
:
new HashMap<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>>()
);
}
/**
* CompilingResultSetMapper with optionally threadSafe cache implementation
*
* @param arrayMaxLength max array/list/map length, a value of less than 1 indicates that all rows from the ResultSet should be included
* @param threadSafe true uses a thread-safe cache implementation (currently ConcurrentHashMap), false uses regular HashMap
*/
public CompilingResultSetMapper(final int arrayMaxLength, final boolean threadSafe) {
this(null, arrayMaxLength, threadSafe);
}
/**
* CompilingResultSetMapper with optionally threadSafe cache implementation
*
* @param threadSafe true uses a thread-safe cache implementation (currently ConcurrentHashMap), false uses regular HashMap
*/
public CompilingResultSetMapper(final boolean threadSafe) {
this(-1, threadSafe);
} }
@Override @Override

View File

@ -1,15 +1,20 @@
package com.moparisthebest.jdbc; package com.moparisthebest.jdbc;
import com.moparisthebest.classgen.Compiler; import com.moparisthebest.classgen.Compiler;
import com.sun.org.apache.xpath.internal.operations.Mod;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.AccessibleObject; import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Calendar; import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Map a ResultSet row to an Object. This mapper generates/compiles/executes java code to perform the mapping. * Map a ResultSet row to an Object. This mapper generates/compiles/executes java code to perform the mapping.
@ -20,7 +25,8 @@ import java.util.Map;
* <p> * <p>
* Usage differences: * Usage differences:
* 1. Reflection can set non-public or final fields directly, direct java code cannot, so DTOs like that will result in * 1. Reflection can set non-public or final fields directly, direct java code cannot, so DTOs like that will result in
* a compilation and therefore mapping error. * a compilation and therefore mapping error, unless the Cache sent in has allowReflection = true which will use reflection
* for these Fields in the generated code.
*/ */
public class CompilingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> { public class CompilingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
@ -32,24 +38,28 @@ public class CompilingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
protected String _calendarName = null; protected String _calendarName = null;
public CompilingRowToObjectMapper(final Compiler compiler, final Map<CompilingRowToObjectMapper.ResultSetKey, ResultSetToObject<?,?>> cache, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) { protected int reflectionFieldIndex = -1;
protected boolean allowReflection = false;
public CompilingRowToObjectMapper(final Compiler compiler, final Cache cache, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
this(compiler, cache, resultSet, returnTypeClass, cal, mapValType, mapKeyType, false); this(compiler, cache, resultSet, returnTypeClass, cal, mapValType, mapKeyType, false);
} }
public CompilingRowToObjectMapper(final Compiler compiler, final Map<CompilingRowToObjectMapper.ResultSetKey, ResultSetToObject<?,?>> cache, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType, final boolean caseInsensitiveMap) { public CompilingRowToObjectMapper(final Compiler compiler, final Cache cache, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType, final boolean caseInsensitiveMap) {
super(resultSet, returnTypeClass, cal, mapValType, mapKeyType, caseInsensitiveMap); super(resultSet, returnTypeClass, cal, mapValType, mapKeyType, caseInsensitiveMap);
this.compiler = compiler; this.compiler = compiler;
try { try {
final CompilingRowToObjectMapper.ResultSetKey keys = new CompilingRowToObjectMapper.ResultSetKey(super.getKeysFromResultSet(), _returnTypeClass, _mapKeyType, cal != null); final CompilingRowToObjectMapper.ResultSetKey keys = new CompilingRowToObjectMapper.ResultSetKey(super.getKeysFromResultSet(), _returnTypeClass, _mapKeyType, cal != null);
//System.out.printf("keys: %s\n", keys); //System.out.printf("keys: %s\n", keys);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final ResultSetToObject<K,T> resultSetToObject = (ResultSetToObject<K,T>) cache.get(keys); final ResultSetToObject<K,T> resultSetToObject = (ResultSetToObject<K,T>) cache.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
if(keys.hasCalendar) if(keys.hasCalendar)
_calendarName = "cal"; _calendarName = "cal";
cache.put(keys, this.resultSetToObject = genClass()); allowReflection = cache.allowReflection;
cache.cache.put(keys, this.resultSetToObject = genClass());
this.keys = null; this.keys = null;
this._fields = null; this._fields = null;
this._fieldTypes = null; this._fieldTypes = null;
@ -109,6 +119,30 @@ public class CompilingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
T toObject(final ResultSet rs, final Calendar cal) throws SQLException; T toObject(final ResultSet rs, final Calendar cal) throws SQLException;
} }
public static class Cache {
private final Map<CompilingRowToObjectMapper.ResultSetKey, ResultSetToObject<?,?>> cache;
private final boolean allowReflection;
public Cache(final Map<ResultSetKey, ResultSetToObject<?, ?>> cache, final boolean allowReflection) {
if(cache == null)
throw new NullPointerException("cache cannot be null");
this.cache = cache;
this.allowReflection = allowReflection;
}
public Cache(final Map<ResultSetKey, ResultSetToObject<?, ?>> cache) {
this(cache, false);
}
public Cache() {
this(new HashMap<CompilingRowToObjectMapper.ResultSetKey, ResultSetToObject<?,?>>());
}
public Cache(final boolean allowReflection) {
this(new HashMap<CompilingRowToObjectMapper.ResultSetKey, ResultSetToObject<?,?>>(), allowReflection);
}
}
protected String typeFromName(final Class<?> type) { protected String typeFromName(final Class<?> type) {
if(type == null) if(type == null)
return "Object"; return "Object";
@ -185,7 +219,21 @@ public class CompilingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
java.append("throw new com.moparisthebest.jdbc.MapperException(com.moparisthebest.jdbc.CompilingRowToObjectMapper.firstColumnError)"); java.append("throw new com.moparisthebest.jdbc.MapperException(com.moparisthebest.jdbc.CompilingRowToObjectMapper.firstColumnError)");
} }
java.append(footer); if(reflectionFieldIndex == -1) {
java.append(footer);
} else {
// otherwise we have a reflection field array to set up...
java.append(";\n }\n\n");
java.append("private static final java.lang.reflect.Field[] _fields = new java.lang.reflect.Field[]{\n");
for(final AccessibleObject ao : _fields)
if(ao instanceof ReflectionAccessibleObject) {
final Field f = ((ReflectionAccessibleObject)ao).field;
java.append("com.moparisthebest.jdbc.util.ReflectionUtil.getAccessibleField(")
.append(f.getDeclaringClass().getCanonicalName()).append(".class, \"")
.append(f.getName()).append("\"),\n");
}
java.append("};\n}\n");
}
//System.out.println(java); //System.out.println(java);
return compiler.compile(className, java); return compiler.compile(className, java);
} }
@ -287,19 +335,21 @@ public class CompilingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
for (int i = 1; i < _fields.length; i++) { for (int i = 1; i < _fields.length; i++) {
AccessibleObject f = _fields[i]; AccessibleObject f = _fields[i];
String enumName = null;
if (_fieldTypes[i] == TypeMappingsFactory.TYPE_ENUM) {
enumName = (f instanceof Field ? ((Field) f).getType() : ((Method) f).getParameterTypes()[0]).getCanonicalName();
}
if (f instanceof Field) { if (f instanceof Field) {
// if f not accessible (but super.getFieldMappings() sets it), throw exception during compilation is fine // if f not accessible (but super.getFieldMappings() sets it), throw exception during compilation is fine
java.append("ret.").append(((Field) f).getName()).append(" = "); java.append("ret.").append(((Field) f).getName()).append(" = ");
extractColumnValueString(java, i, _fieldTypes[i], enumName); extractColumnValueString(java, i, _fieldTypes[i],
_fieldTypes[i] == TypeMappingsFactory.TYPE_ENUM ? ((Field) f).getType().getCanonicalName() : null);
java.append(";\n"); java.append(";\n");
} else if (f instanceof ReflectionAccessibleObject) {
java.append("com.moparisthebest.jdbc.util.ReflectionUtil.setValue(_fields[").append(String.valueOf(((ReflectionAccessibleObject)f).index)).append("], ret, ");
extractColumnValueString(java, i, _fieldTypes[i],
_fieldTypes[i] == TypeMappingsFactory.TYPE_ENUM ? ((ReflectionAccessibleObject) f).field.getType().getCanonicalName() : null);
java.append(");\n");
} else { } else {
java.append("ret.").append(((Method) f).getName()).append("("); java.append("ret.").append(((Method) f).getName()).append("(");
extractColumnValueString(java, i, _fieldTypes[i], enumName); extractColumnValueString(java, i, _fieldTypes[i],
_fieldTypes[i] == TypeMappingsFactory.TYPE_ENUM ? ((Method) f).getParameterTypes()[0].getCanonicalName() : null);
java.append(");\n"); java.append(");\n");
} }
} }
@ -308,6 +358,17 @@ public class CompilingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
java.append("ret.finish(rs);\n"); java.append("ret.finish(rs);\n");
} }
@Override
protected AccessibleObject modField(final Field field, final int index) {
if(!allowReflection)
return field;
final int modifiers = field.getModifiers();
if(Modifier.isFinal(modifiers) || Modifier.isPrivate(modifiers) || Modifier.isProtected(modifiers)) {
return new ReflectionAccessibleObject(field, ++reflectionFieldIndex);
}
return field;
}
public void extractColumnValueString(final Appendable java, final int index, final int resultType, final String enumName) throws IOException { public void extractColumnValueString(final Appendable java, final int index, final int resultType, final String enumName) throws IOException {
extractColumnValueString(java, index, resultType, enumName, _calendarName); extractColumnValueString(java, index, resultType, enumName, _calendarName);
} }

View File

@ -0,0 +1,17 @@
package com.moparisthebest.jdbc;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
/**
* Created by mopar on 6/19/17.
*/
class ReflectionAccessibleObject extends AccessibleObject {
final Field field;
final int index;
public ReflectionAccessibleObject(final Field field, final int index) {
this.field = field;
this.index = index;
}
}

View File

@ -452,16 +452,22 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
+ "names and public setter methods on the return class. Columns are also " + "names and public setter methods on the return class. Columns are also "
+ "stripped of '_' and compared if no match is found with them."); + "stripped of '_' and compared if no match is found with them.");
} }
f.setAccessible(true);
_fields[i] = f; _fields[i] = f;
if (f instanceof Field) { if (f instanceof Field) {
_fieldTypes[i] = _tmf.getTypeId(((Field) f).getType()); final Field field = (Field) f;
_fields[i] = modField(field, i);
_fieldTypes[i] = _tmf.getTypeId(field.getType());
} else { } else {
_fieldTypes[i] = _tmf.getTypeId(((Method) f).getParameterTypes()[0]); _fieldTypes[i] = _tmf.getTypeId(((Method) f).getParameterTypes()[0]);
} }
} }
} }
protected AccessibleObject modField(final Field field, final int index) {
field.setAccessible(true);
return field;
}
public static <T> T fixNull(Class<T> returnType) { public static <T> T fixNull(Class<T> returnType) {
return returnType.cast(_tmf.fixNull(returnType)); return returnType.cast(_tmf.fixNull(returnType));
} }

View File

@ -1,15 +1,15 @@
package com.moparisthebest.jdbc; package com.moparisthebest.jdbc;
import com.moparisthebest.jdbc.util.CacheUtil;
import java.util.Calendar; import java.util.Calendar;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Global for entire application, hopefully you know what you are doing. * Global for entire application, hopefully you know what you are doing.
*/ */
public class StaticCompilingResultSetMapper extends CompilingResultSetMapper { public class StaticCompilingResultSetMapper extends CompilingResultSetMapper {
private static final Map<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>> cache = new ConcurrentHashMap<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>>(); private static final CompilingRowToObjectMapper.Cache cache = new CompilingRowToObjectMapper.Cache(CacheUtil.<CompilingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?,?>>getCache(true), true);
public static final StaticCompilingResultSetMapper instance = new StaticCompilingResultSetMapper(); public static final StaticCompilingResultSetMapper instance = new StaticCompilingResultSetMapper();

View File

@ -0,0 +1,35 @@
package com.moparisthebest.jdbc.util;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created by mopar on 6/19/17.
*/
public abstract class CacheUtil {
public static <K, V> Map<K, V> getCache(final int maxEntries) {
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;
return new LinkedHashMap<K, V>(initialCapacity, loadFactor, true) {
@Override
protected boolean removeEldestEntry(final Map.Entry<K, V> eldest) {
return size() > maxEntries;
}
};
} else
return new HashMap<K, V>();
}
public static <K, V> Map<K, V> getCache(final boolean threadSafe) {
return threadSafe ?
new ConcurrentHashMap<K, V>()
:
new HashMap<K, V>();
}
}

View File

@ -146,7 +146,7 @@ public class QueryMapperTest {
{ new ResultSetMapper() }, { new ResultSetMapper() },
{ new CachingResultSetMapper() }, { new CachingResultSetMapper() },
{ new CaseInsensitiveMapResultSetMapper() }, { new CaseInsensitiveMapResultSetMapper() },
{ new CompilingResultSetMapper() }, { new CompilingResultSetMapper(new CompilingRowToObjectMapper.Cache(true)) },
}); });
} }