mirror of
https://github.com/moparisthebest/JdbcMapper
synced 2024-11-22 00:52:16 -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>
|
<testExcludes>
|
||||||
<exclude>**/module-info.java</exclude>
|
<exclude>**/module-info.java</exclude>
|
||||||
</testExcludes>
|
</testExcludes>
|
||||||
|
<compilerArgs>
|
||||||
|
<compilerArg>-parameters</compilerArg>
|
||||||
|
<compilerArg>-Xlint:unchecked</compilerArg>
|
||||||
|
</compilerArgs>
|
||||||
</configuration>
|
</configuration>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>testCompile</goal>
|
<goal>testCompile</goal>
|
||||||
|
@ -35,20 +35,46 @@ public class CachingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void getFieldMappings() throws SQLException {
|
protected void lazyLoadConstructor() throws SQLException {
|
||||||
final FieldMapping<T> fm = cache.get(keys);
|
final FieldMapping<T> fm = cache.get(keys);
|
||||||
if (fm == null) {
|
if (fm == 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
|
||||||
super.getFieldMappings();
|
super.lazyLoadConstructor();
|
||||||
cache.put(keys, new FieldMapping<T>(_fields, _fieldTypes, resultSetConstructor, constructor));
|
cache.put(keys, new FieldMapping<T>(_fields, _fieldTypes, _fieldOrder, _fieldClasses, resultSetConstructor, constructor));
|
||||||
} else {
|
} else {
|
||||||
//System.out.printf("cache hit, keys: %s\n", keys);
|
//System.out.printf("cache hit, keys: %s\n", keys);
|
||||||
// load from cache
|
// load from cache
|
||||||
_fields = fm._fields;
|
_fields = fm._fields;
|
||||||
_fieldTypes = fm._fieldTypes;
|
_fieldTypes = fm._fieldTypes;
|
||||||
|
_fieldOrder = fm._fieldOrder;
|
||||||
|
_fieldClasses = fm._fieldClasses;
|
||||||
resultSetConstructor = fm.resultSetConstructor;
|
resultSetConstructor = fm.resultSetConstructor;
|
||||||
constructor = fm.constructor;
|
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;
|
constructorLoaded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,13 +123,16 @@ public class CachingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
|
|||||||
|
|
||||||
static class FieldMapping<T> {
|
static class FieldMapping<T> {
|
||||||
public final AccessibleObject[] _fields;
|
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 boolean resultSetConstructor;
|
||||||
public final Constructor<? extends T> constructor;
|
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._fields = _fields;
|
||||||
this._fieldTypes = _fieldTypes;
|
this._fieldTypes = _fieldTypes;
|
||||||
|
this._fieldOrder = _fieldOrder;
|
||||||
|
this._fieldClasses = _fieldClasses;
|
||||||
this.resultSetConstructor = resultSetConstructor;
|
this.resultSetConstructor = resultSetConstructor;
|
||||||
this.constructor = constructor;
|
this.constructor = constructor;
|
||||||
}
|
}
|
||||||
|
@ -246,13 +246,29 @@ public class CompilingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lazyLoadConstructor();
|
try {
|
||||||
|
lazyLoadConstructor();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new MapperException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
if (resultSetConstructor) {
|
if (resultSetConstructor) {
|
||||||
java.append("final ").append(tType).append(" ret = new ").append(tType).append("(rs);\n");
|
java.append("final ").append(tType).append(" ret = new ").append(tType).append("(rs);\n");
|
||||||
return;
|
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
|
if (returnMap) // we want a map
|
||||||
try {
|
try {
|
||||||
java.append("final ").append(tType).append("<String, Object> ret = new ").append(tType).append("<String, Object>();\n");
|
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
|
// 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],
|
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");
|
java.append(";\n");
|
||||||
} else if (f instanceof ReflectionAccessibleObject) {
|
} else if (f instanceof ReflectionAccessibleObject) {
|
||||||
java.append("com.moparisthebest.jdbc.util.ReflectionUtil.setValue(_fields[").append(String.valueOf(((ReflectionAccessibleObject)f).index)).append("], ret, ");
|
java.append("com.moparisthebest.jdbc.util.ReflectionUtil.setValue(_fields[").append(String.valueOf(((ReflectionAccessibleObject)f).index)).append("], ret, ");
|
||||||
extractColumnValueString(java, i, _fieldTypes[i],
|
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");
|
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],
|
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");
|
java.append(");\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import java.lang.reflect.*;
|
|||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.ResultSetMetaData;
|
import java.sql.ResultSetMetaData;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -73,9 +74,10 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
|||||||
protected final boolean returnMap;
|
protected final boolean returnMap;
|
||||||
|
|
||||||
protected AccessibleObject[] _fields = null;
|
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) {
|
public RowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass) {
|
||||||
this(resultSet, returnTypeClass, null, null);
|
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)
|
if(constructorLoaded)
|
||||||
return;
|
return;
|
||||||
// detect if returnTypeClass has a constructor that takes a ResultSet, if so, our job couldn't be easier...
|
// 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;
|
resultSetConstructor = true;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
// if no resultSetConstructor find the constructor
|
// if no resultSetConstructor find the constructor
|
||||||
try {
|
|
||||||
constructor = _returnTypeClass.getDeclaredConstructor();
|
// use con.getParameterAnnotations(); for java 6? ugly
|
||||||
if (!constructor.isAccessible())
|
//IFJAVA8_START
|
||||||
constructor.setAccessible(true);
|
// look for constructor with matching parameters first
|
||||||
} catch (Throwable e1) {
|
String[] keys = null;
|
||||||
// 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
|
Map<String, Integer> strippedKeys = null;
|
||||||
// or if componentType is non-null, then we want an array like Long[] or String[]
|
outer:
|
||||||
if(_columnCount > 2 && componentType == null)
|
for(final Constructor<?> con : _returnTypeClass.getConstructors()) {
|
||||||
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);
|
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.resultSetConstructor = resultSetConstructor;
|
||||||
this.constructor = constructor;
|
this.constructor = constructor;
|
||||||
|
if(this._args == null)
|
||||||
|
this._args = new Object[1];
|
||||||
this.constructorLoaded = true;
|
this.constructorLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,6 +246,17 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
|||||||
+ _returnTypeClass.getName() + " sending in a ResultSet object as a parameter", e);
|
+ _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
|
if(returnMap) // we want a map
|
||||||
try {
|
try {
|
||||||
final Map<String, Object> ret = getMapImplementation();
|
final Map<String, Object> ret = getMapImplementation();
|
||||||
@ -271,7 +334,7 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
|||||||
AccessibleObject f = _fields[i];
|
AccessibleObject f = _fields[i];
|
||||||
|
|
||||||
try {
|
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]);
|
//System.out.printf("field: '%s' obj: '%s' fieldType: '%s'\n", _fields[i], _args[0], _fieldTypes[i]);
|
||||||
if (f instanceof Field) {
|
if (f instanceof Field) {
|
||||||
((Field) f).set(resultObject, _args[0]);
|
((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
|
// finally actually init the fields array
|
||||||
_fields = new AccessibleObject[_columnCount + 1];
|
_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++) {
|
for (int i = 1; i < _fields.length; i++) {
|
||||||
AccessibleObject f = mapFields.get(keys[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;
|
final Field field = (Field) f;
|
||||||
_fields[i] = modField(field, i);
|
_fields[i] = modField(field, i);
|
||||||
_fieldTypes[i] = _tmf.getTypeId(field.getType());
|
_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 {
|
} else {
|
||||||
_fieldTypes[i] = _tmf.getTypeId(((Method) f).getParameterTypes()[0]);
|
_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
|
* @return The extracted value
|
||||||
* @throws java.sql.SQLException on error.
|
* @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{
|
try{
|
||||||
switch (resultType) {
|
switch (resultType) {
|
||||||
case TypeMappingsFactory.TYPE_INT:
|
case TypeMappingsFactory.TYPE_INT:
|
||||||
@ -577,11 +651,6 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
|||||||
case TypeMappingsFactory.TYPE_XMLBEAN_ENUM:
|
case TypeMappingsFactory.TYPE_XMLBEAN_ENUM:
|
||||||
return _resultSet.getString(index);
|
return _resultSet.getString(index);
|
||||||
case TypeMappingsFactory.TYPE_ENUM:
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
final Enum ret = Enum.valueOf((Class<? extends Enum>)resultTypeClass, _resultSet.getString(index));
|
final Enum ret = Enum.valueOf((Class<? extends Enum>)resultTypeClass, _resultSet.getString(index));
|
||||||
return ret;
|
return ret;
|
||||||
|
Loading…
Reference in New Issue
Block a user