diff --git a/pom.xml b/pom.xml
index 45dd4ed..b829864 100644
--- a/pom.xml
+++ b/pom.xml
@@ -299,6 +299,10 @@
**/module-info.java
+
+ -parameters
+ -Xlint:unchecked
+
testCompile
diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/CachingRowToObjectMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/CachingRowToObjectMapper.java
index 579036c..b299217 100644
--- a/querymapper/src/main/java/com/moparisthebest/jdbc/CachingRowToObjectMapper.java
+++ b/querymapper/src/main/java/com/moparisthebest/jdbc/CachingRowToObjectMapper.java
@@ -35,20 +35,46 @@ public class CachingRowToObjectMapper extends RowToObjectMapper {
}
@Override
- protected void getFieldMappings() throws SQLException {
+ protected void lazyLoadConstructor() throws SQLException {
final FieldMapping 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(_fields, _fieldTypes, resultSetConstructor, constructor));
+ super.lazyLoadConstructor();
+ cache.put(keys, new FieldMapping(_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 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(_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 extends RowToObjectMapper {
static class FieldMapping {
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;
}
diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java
index 4a08f55..076471f 100644
--- a/querymapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java
+++ b/querymapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java
@@ -246,13 +246,29 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper {
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(" ret = new ").append(tType).append("();\n");
@@ -338,17 +354,17 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper {
// 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");
}
}
diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java
index 99d42fe..4ca5be3 100644
--- a/querymapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java
+++ b/querymapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java
@@ -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 extends AbstractRowMapper {
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 returnTypeClass) {
this(resultSet, returnTypeClass, null, null);
@@ -129,7 +131,7 @@ public class RowToObjectMapper extends AbstractRowMapper {
}
}
- 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 extends AbstractRowMapper {
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 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(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 extends AbstractRowMapper {
} 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 ret = getMapImplementation();
@@ -271,7 +334,7 @@ public class RowToObjectMapper extends AbstractRowMapper {
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 extends AbstractRowMapper {
// 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 extends AbstractRowMapper {
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 extends AbstractRowMapper {
* @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 extends AbstractRowMapper {
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;