Add constructor calling for jdbcmapper and discover bug in javac 1.8 with -parameters option

This commit is contained in:
Travis Burtrum 2017-06-19 22:37:44 -04:00
parent d2df6dabaf
commit df2ccda9fa
3 changed files with 65 additions and 10 deletions

View File

@ -49,7 +49,8 @@ public class CompileTimeRowToObjectMapper {
protected final boolean returnMap, resultSetConstructor;
protected Element[] _fields = null;
protected int[] _fieldTypes;
protected int[] _fieldTypes, _fieldOrder;
protected String[] _fieldClasses;
protected final ReflectionFields reflectionFields;
@ -78,21 +79,62 @@ public class CompileTimeRowToObjectMapper {
componentType = returnTypeClass.getKind() == TypeKind.ARRAY ? ((ArrayType) returnTypeClass).getComponentType() : null;
// detect if returnTypeClass has a constructor that takes a ResultSet, if so, our job couldn't be easier...
boolean resultSetConstructor = false, defaultConstructor = false;
boolean resultSetConstructor = false, defaultConstructor = false, paramConstructor = false;
if(_returnTypeClass.getKind() == TypeKind.DECLARED) {
Map<String, Integer> strippedKeys = null;
final List<? extends Element> methodsAndConstructors = ((TypeElement)((DeclaredType)_returnTypeClass).asElement()).getEnclosedElements();
/*
// uncomment this to show difference between java 1.8 with -parameters and not, prints this without -parameters (javac 1.6 gets this correct also):
// methodsAndConstructors: FieldPerson(): '', FieldPerson(long,java.util.Date,java.lang.String,java.lang.String): 'long PERSONNO, java.util.Date BIRTHDATE, java.lang.String FIRSTNAME, java.lang.String LASTNAME', FieldPerson(com.moparisthebest.jdbc.dto.Person): 'com.moparisthebest.jdbc.dto.Person PERSON'
// but javac 1.8 prints this with -parameters (wrongly):
// methodsAndConstructors: FieldPerson(): '', FieldPerson(long,java.util.Date,java.lang.String,java.lang.String): 'long PERSONNO, java.util.Date FIRSTNAME, java.lang.String LASTNAME, java.lang.String ARG3', FieldPerson(com.moparisthebest.jdbc.dto.Person): 'com.moparisthebest.jdbc.dto.Person PERSON'
if(_returnTypeClass.toString().equals("com.moparisthebest.jdbc.dto.FieldPerson"))
throw new RuntimeException("methodsAndConstructors: " + methodsAndConstructors.stream().filter(e -> e.getKind() == ElementKind.CONSTRUCTOR && e.getModifiers().contains(Modifier.PUBLIC)).map(e -> e.toString() +
": '" + ((ExecutableElement)e).getParameters().stream().map(param -> param.asType() + " " + param.getSimpleName().toString().toUpperCase()).collect(java.util.stream.Collectors.joining(", ")) + "'"
).collect(java.util.stream.Collectors.joining(", ")));
*/
outer:
for(final Element e : methodsAndConstructors) {
if(e.getKind() == ElementKind.CONSTRUCTOR) {
if(e.getKind() == ElementKind.CONSTRUCTOR && e.getModifiers().contains(Modifier.PUBLIC)) {
final List<? extends VariableElement> params = ((ExecutableElement)e).getParameters();
if(params.isEmpty())
defaultConstructor = true;
else if(params.size() == 1 && rsm.types.isSameType(params.get(0).asType(), rsm.resultSetType))
resultSetConstructor = true;
else if(params.size() == _columnCount) {
// maybe we want to call the constructor, if the names line up
if(strippedKeys == null) {
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];
_fieldClasses = new String[keys.length];
}
int count = 0;
for(final VariableElement param : params) {
final Integer index = strippedKeys.get(param.getSimpleName().toString().toUpperCase());
if(index == null)
continue outer;
_fieldOrder[++count] = index;
_fieldTypes[count] = getTypeId(param.asType());
if(_fieldTypes[count] == TypeMappingsFactory.TYPE_ENUM) {
_fieldClasses[count] = param.asType().toString();
}
}
paramConstructor = true;
}
}
}
}
if(!paramConstructor)
_fieldOrder = null; // didn't successfully finish
this.resultSetConstructor = resultSetConstructor;
if(!resultSetConstructor && !defaultConstructor && _columnCount > 2 && componentType == null)
if(!resultSetConstructor && !defaultConstructor && !paramConstructor && _columnCount > 2 && componentType == null)
throw new RuntimeException("Exception when trying to get constructor for : "+_returnTypeClass.toString() + " Must have default no-arg constructor or one that takes a single ResultSet.");
}
}
@ -270,6 +312,17 @@ public class CompileTimeRowToObjectMapper {
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]);
if(x != _columnCount)
java.append(",\n");
}
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");

View File

@ -44,12 +44,12 @@ public interface PersonDAO extends Closeable {
@JdbcMapper.SQL("SELECT first_name FROM person WHERE person_no = {personNo}")
String getFirstName(long personNo) throws SQLException;
@JdbcMapper.SQL("SELECT first_name, last_name, birth_date FROM person WHERE person_no = {personNo}")
FieldPerson getPerson(long personNo, Calendar cal) throws SQLException;
@JdbcMapper.SQL(value = "SELECT person_no, first_name, last_name, birth_date FROM person WHERE person_no = {personNo}")
FieldPerson getPerson(long personNo) throws SQLException;
@JdbcMapper.SQL("SELECT first_name, last_name, birth_date FROM person WHERE person_no = {personNo}")
FieldPerson getPerson(long personNo, Calendar cal) throws SQLException;
@JdbcMapper.SQL("SELECT first_name, last_name FROM person WHERE last_name = {lastName}")
List<FieldPerson> getPeople(String lastName) throws SQLException;

View File

@ -191,7 +191,8 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
}
}
//IFJAVA8_END
if(constructor == null)
if(constructor == null) {
_fieldOrder = null; // we didn't complete this...
try {
constructor = _returnTypeClass.getDeclaredConstructor();
if (!constructor.isAccessible())
@ -199,9 +200,10 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
} 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);
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;