mirror of
https://github.com/moparisthebest/JdbcMapper
synced 2024-12-21 23:08:52 -05:00
Add constructor calling for querymapper in java8+ only for now
This commit is contained in:
parent
46686e5b05
commit
d2df6dabaf
4
pom.xml
4
pom.xml
@ -299,6 +299,10 @@
|
||||
<testExcludes>
|
||||
<exclude>**/module-info.java</exclude>
|
||||
</testExcludes>
|
||||
<compilerArgs>
|
||||
<compilerArg>-parameters</compilerArg>
|
||||
<compilerArg>-Xlint:unchecked</compilerArg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>testCompile</goal>
|
||||
|
@ -35,20 +35,46 @@ public class CachingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getFieldMappings() throws SQLException {
|
||||
protected void lazyLoadConstructor() throws SQLException {
|
||||
final FieldMapping<T> fm = cache.get(keys);
|
||||
if (fm == null) {
|
||||
//System.out.printf("cache miss, keys: %s\n", keys);
|
||||
// generate and put into cache
|
||||
super.getFieldMappings();
|
||||
cache.put(keys, new FieldMapping<T>(_fields, _fieldTypes, resultSetConstructor, constructor));
|
||||
super.lazyLoadConstructor();
|
||||
cache.put(keys, new FieldMapping<T>(_fields, _fieldTypes, _fieldOrder, _fieldClasses, resultSetConstructor, constructor));
|
||||
} else {
|
||||
//System.out.printf("cache hit, keys: %s\n", keys);
|
||||
// load from cache
|
||||
_fields = fm._fields;
|
||||
_fieldTypes = fm._fieldTypes;
|
||||
_fieldOrder = fm._fieldOrder;
|
||||
_fieldClasses = fm._fieldClasses;
|
||||
resultSetConstructor = fm.resultSetConstructor;
|
||||
constructor = fm.constructor;
|
||||
_args = _fieldOrder == null ? new Object[1] : new Object[_columnCount];
|
||||
constructorLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getFieldMappings() throws SQLException {
|
||||
final FieldMapping<T> fm = cache.get(keys);
|
||||
// todo: could cache fm from lazyLoadConstructor and only set _fields if non-final, but race? investigate
|
||||
if (fm == null || fm._fields == null) {
|
||||
//System.out.printf("cache miss, keys: %s\n", keys);
|
||||
// generate and put into cache
|
||||
super.getFieldMappings();
|
||||
cache.put(keys, new FieldMapping<T>(_fields, _fieldTypes, _fieldOrder, _fieldClasses, resultSetConstructor, constructor));
|
||||
} else {
|
||||
//System.out.printf("cache hit, keys: %s\n", keys);
|
||||
// load from cache
|
||||
_fields = fm._fields;
|
||||
_fieldTypes = fm._fieldTypes;
|
||||
_fieldOrder = fm._fieldOrder;
|
||||
_fieldClasses = fm._fieldClasses;
|
||||
resultSetConstructor = fm.resultSetConstructor;
|
||||
constructor = fm.constructor;
|
||||
_args = _fieldOrder == null ? new Object[1] : new Object[_columnCount];
|
||||
constructorLoaded = true;
|
||||
}
|
||||
}
|
||||
@ -97,13 +123,16 @@ public class CachingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
|
||||
|
||||
static class FieldMapping<T> {
|
||||
public final AccessibleObject[] _fields;
|
||||
public final int[] _fieldTypes;
|
||||
public final int[] _fieldTypes, _fieldOrder;
|
||||
public final Class<? extends Enum>[] _fieldClasses;
|
||||
public final boolean resultSetConstructor;
|
||||
public final Constructor<? extends T> constructor;
|
||||
|
||||
public FieldMapping(final AccessibleObject[] _fields, final int[] _fieldTypes, final boolean resultSetConstructor, final Constructor<? extends T> constructor) {
|
||||
public FieldMapping(final AccessibleObject[] _fields, final int[] _fieldTypes, final int[] _fieldOrder, final Class<? extends Enum>[] _fieldClasses, final boolean resultSetConstructor, final Constructor<? extends T> constructor) {
|
||||
this._fields = _fields;
|
||||
this._fieldTypes = _fieldTypes;
|
||||
this._fieldOrder = _fieldOrder;
|
||||
this._fieldClasses = _fieldClasses;
|
||||
this.resultSetConstructor = resultSetConstructor;
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
@ -246,13 +246,29 @@ public class CompilingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
|
||||
return;
|
||||
}
|
||||
|
||||
lazyLoadConstructor();
|
||||
try {
|
||||
lazyLoadConstructor();
|
||||
} catch (SQLException e) {
|
||||
throw new MapperException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (resultSetConstructor) {
|
||||
java.append("final ").append(tType).append(" ret = new ").append(tType).append("(rs);\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(_fieldOrder != null) {
|
||||
java.append("final ").append(tType).append(" ret = new ").append(tType).append("(\n");
|
||||
for(int x = 1; x <= _columnCount; ++x) {
|
||||
extractColumnValueString(java, _fieldOrder[x], _fieldTypes[x], _fieldClasses[x] == null ? null : _fieldClasses[x].getCanonicalName());
|
||||
if(x != _columnCount)
|
||||
java.append(",\n");
|
||||
//_args[x-1] = extractColumnValue(_fieldOrder[x], _fieldTypes[x], _fieldClasses[x]);
|
||||
}
|
||||
java.append(");\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (returnMap) // we want a map
|
||||
try {
|
||||
java.append("final ").append(tType).append("<String, Object> ret = new ").append(tType).append("<String, Object>();\n");
|
||||
@ -338,17 +354,17 @@ public class CompilingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
|
||||
// if f not accessible (but super.getFieldMappings() sets it), throw exception during compilation is fine
|
||||
java.append("ret.").append(((Field) f).getName()).append(" = ");
|
||||
extractColumnValueString(java, i, _fieldTypes[i],
|
||||
_fieldTypes[i] == TypeMappingsFactory.TYPE_ENUM ? ((Field) f).getType().getCanonicalName() : null);
|
||||
_fieldClasses[i] == null ? null : _fieldClasses[i].getCanonicalName());
|
||||
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);
|
||||
_fieldClasses[i] == null ? null : _fieldClasses[i].getCanonicalName());
|
||||
java.append(");\n");
|
||||
} else {
|
||||
java.append("ret.").append(((Method) f).getName()).append("(");
|
||||
extractColumnValueString(java, i, _fieldTypes[i],
|
||||
_fieldTypes[i] == TypeMappingsFactory.TYPE_ENUM ? ((Method) f).getParameterTypes()[0].getCanonicalName() : null);
|
||||
_fieldClasses[i] == null ? null : _fieldClasses[i].getCanonicalName());
|
||||
java.append(");\n");
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import java.lang.reflect.*;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@ -67,15 +68,16 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
||||
protected boolean resultSetConstructor, constructorLoaded = false;
|
||||
protected Constructor<? extends T> constructor;
|
||||
protected final Class<? extends T> _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 = null;
|
||||
protected int[] _fieldTypes;
|
||||
protected int[] _fieldTypes, _fieldOrder;
|
||||
protected Class<? extends Enum>[] _fieldClasses;
|
||||
|
||||
protected final Object[] _args = new Object[1];
|
||||
protected Object[] _args;
|
||||
|
||||
public RowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass) {
|
||||
this(resultSet, returnTypeClass, null, null);
|
||||
@ -129,7 +131,7 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
||||
}
|
||||
}
|
||||
|
||||
protected void lazyLoadConstructor() {
|
||||
protected void lazyLoadConstructor() throws SQLException {
|
||||
if(constructorLoaded)
|
||||
return;
|
||||
// detect if returnTypeClass has a constructor that takes a ResultSet, if so, our job couldn't be easier...
|
||||
@ -142,19 +144,69 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
||||
resultSetConstructor = true;
|
||||
} catch (Throwable e) {
|
||||
// if no resultSetConstructor find the constructor
|
||||
try {
|
||||
constructor = _returnTypeClass.getDeclaredConstructor();
|
||||
if (!constructor.isAccessible())
|
||||
constructor.setAccessible(true);
|
||||
} catch (Throwable e1) {
|
||||
// if column count is 2 or less, it might map directly to a type like a Long or something, or be a map which does
|
||||
// or if componentType is non-null, then we want an array like Long[] or String[]
|
||||
if(_columnCount > 2 && componentType == null)
|
||||
throw new MapperException("Exception when trying to get constructor for : "+_returnTypeClass.getName() + " Must have default no-arg constructor or one that takes a single ResultSet.", e1);
|
||||
|
||||
// use con.getParameterAnnotations(); for java 6? ugly
|
||||
//IFJAVA8_START
|
||||
// look for constructor with matching parameters first
|
||||
String[] keys = null;
|
||||
Map<String, Integer> strippedKeys = null;
|
||||
outer:
|
||||
for(final Constructor<?> con : _returnTypeClass.getConstructors()) {
|
||||
final Parameter[] params = con.getParameters();
|
||||
if(params.length == _columnCount) {
|
||||
if(!params[0].isNamePresent())
|
||||
break; // nothing to do here, compile with -params?
|
||||
// do this stuff only once
|
||||
if(keys == null) {
|
||||
keys = getKeysFromResultSet();
|
||||
strippedKeys = new HashMap<String, Integer>(keys.length * 2);
|
||||
for (int x = 1; x <= _columnCount; ++x) {
|
||||
final String key = keys[x];
|
||||
strippedKeys.put(key, x);
|
||||
strippedKeys.put(key.replaceAll("_", ""), x);
|
||||
}
|
||||
_fieldOrder = new int[keys.length];
|
||||
_fieldTypes = new int[keys.length];
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<? extends Enum>[] noWarnings = (Class<? extends Enum>[]) new Class[keys.length];
|
||||
_fieldClasses = noWarnings;
|
||||
}
|
||||
int count = 0;
|
||||
for(final Parameter param : params) {
|
||||
final Integer index = strippedKeys.get(param.getName().toUpperCase());
|
||||
if(index == null)
|
||||
continue outer;
|
||||
_fieldOrder[++count] = index;
|
||||
_fieldTypes[count] = _tmf.getTypeId(param.getType());
|
||||
if(_fieldTypes[count] == TypeMappingsFactory.TYPE_ENUM) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<? extends Enum> noWarnings = (Class<? extends Enum>) param.getType();
|
||||
_fieldClasses[count] = noWarnings;
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
final Constructor<? extends T> noWarnings = (Constructor<? extends T>)con;
|
||||
this._args = new Object[params.length];
|
||||
constructor = noWarnings;
|
||||
}
|
||||
}
|
||||
//IFJAVA8_END
|
||||
if(constructor == null)
|
||||
try {
|
||||
constructor = _returnTypeClass.getDeclaredConstructor();
|
||||
if (!constructor.isAccessible())
|
||||
constructor.setAccessible(true);
|
||||
} catch (Throwable e1) {
|
||||
// if column count is 2 or less, it might map directly to a type like a Long or something, or be a map which does
|
||||
// or if componentType is non-null, then we want an array like Long[] or String[]
|
||||
if(_columnCount > 2 && componentType == null)
|
||||
throw new MapperException("Exception when trying to get constructor for : "+_returnTypeClass.getName() + " Must have default no-arg constructor or one that takes a single ResultSet.", e1);
|
||||
}
|
||||
}
|
||||
this.resultSetConstructor = resultSetConstructor;
|
||||
this.constructor = constructor;
|
||||
if(this._args == null)
|
||||
this._args = new Object[1];
|
||||
this.constructorLoaded = true;
|
||||
}
|
||||
|
||||
@ -192,8 +244,19 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
||||
} 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(_fieldOrder != null)
|
||||
try {
|
||||
for(int x = 1; x <= _columnCount; ++x)
|
||||
_args[x-1] = extractColumnValue(_fieldOrder[x], _fieldTypes[x], _fieldClasses[x]);
|
||||
//System.out.println("creating " + constructor + " sending in a objects: " + Arrays.toString(_args));
|
||||
return constructor.newInstance(_args);
|
||||
} catch (Throwable e) {
|
||||
throw new MapperException(e.getClass().getName() + " when trying to create instance of : "
|
||||
+ _returnTypeClass.getName() + " sending in a objects: " + Arrays.toString(_args), e);
|
||||
}
|
||||
|
||||
if(returnMap) // we want a map
|
||||
try {
|
||||
final Map<String, Object> ret = getMapImplementation();
|
||||
@ -271,7 +334,7 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
||||
AccessibleObject f = _fields[i];
|
||||
|
||||
try {
|
||||
_args[0] = extractColumnValue(i, _fieldTypes[i], null); // be lazy about this
|
||||
_args[0] = extractColumnValue(i, _fieldTypes[i], _fieldClasses[i]);
|
||||
//System.out.printf("field: '%s' obj: '%s' fieldType: '%s'\n", _fields[i], _args[0], _fieldTypes[i]);
|
||||
if (f instanceof Field) {
|
||||
((Field) f).set(resultObject, _args[0]);
|
||||
@ -440,7 +503,13 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
||||
|
||||
// finally actually init the fields array
|
||||
_fields = new AccessibleObject[_columnCount + 1];
|
||||
_fieldTypes = new int[_columnCount + 1];
|
||||
if(_fieldTypes == null)
|
||||
_fieldTypes = new int[_fields.length];
|
||||
if(_fieldClasses == null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<? extends Enum>[] noWarnings = (Class<? extends Enum>[]) new Class[_fields.length];
|
||||
_fieldClasses = noWarnings;
|
||||
}
|
||||
|
||||
for (int i = 1; i < _fields.length; i++) {
|
||||
AccessibleObject f = mapFields.get(keys[i]);
|
||||
@ -457,6 +526,11 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
||||
final Field field = (Field) f;
|
||||
_fields[i] = modField(field, i);
|
||||
_fieldTypes[i] = _tmf.getTypeId(field.getType());
|
||||
if(_fieldTypes[i] == TypeMappingsFactory.TYPE_ENUM) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<? extends Enum> noWarnings = (Class<? extends Enum>) field.getType();
|
||||
_fieldClasses[i] = noWarnings;
|
||||
}
|
||||
} else {
|
||||
_fieldTypes[i] = _tmf.getTypeId(((Method) f).getParameterTypes()[0]);
|
||||
}
|
||||
@ -503,7 +577,7 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
||||
* @return The extracted value
|
||||
* @throws java.sql.SQLException on error.
|
||||
*/
|
||||
protected Object extractColumnValue(final int index, final int resultType, Class<?> resultTypeClass) throws SQLException {
|
||||
protected Object extractColumnValue(final int index, final int resultType, final Class<?> resultTypeClass) throws SQLException {
|
||||
try{
|
||||
switch (resultType) {
|
||||
case TypeMappingsFactory.TYPE_INT:
|
||||
@ -577,11 +651,6 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
||||
case TypeMappingsFactory.TYPE_XMLBEAN_ENUM:
|
||||
return _resultSet.getString(index);
|
||||
case TypeMappingsFactory.TYPE_ENUM:
|
||||
if(resultTypeClass == null) {
|
||||
// load lazily, todo: could cache? meh
|
||||
final AccessibleObject f = _fields[index];
|
||||
resultTypeClass = f instanceof Field ? ((Field)f).getType() : ((Method)f).getParameterTypes()[0];
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
final Enum ret = Enum.valueOf((Class<? extends Enum>)resultTypeClass, _resultSet.getString(index));
|
||||
return ret;
|
||||
|
Loading…
Reference in New Issue
Block a user