Call super(Connection) if it exists, tweak cannot find Constructor error message

This commit is contained in:
Travis Burtrum 2018-03-17 00:42:22 -04:00
parent 50ebcc84cb
commit f68fd749e3
4 changed files with 57 additions and 10 deletions

View File

@ -2,6 +2,7 @@ package com.moparisthebest.jdbc.codegen;
import com.moparisthebest.jdbc.CompilingRowToObjectMapper;
import com.moparisthebest.jdbc.MapperException;
import com.moparisthebest.jdbc.RowToObjectMapper;
import com.moparisthebest.jdbc.TypeMappingsFactory;
import javax.lang.model.element.*;
@ -95,7 +96,7 @@ public class CompileTimeRowToObjectMapper {
*/
outer:
for(final Element e : methodsAndConstructors) {
if(e.getKind() == ElementKind.CONSTRUCTOR && e.getModifiers().contains(Modifier.PUBLIC)) {
if(e.getKind() == ElementKind.CONSTRUCTOR && e.getModifiers().contains(Modifier.PUBLIC)) { // todo: public normally, but also package-private if same package, or protected if it's a sub-class of super...
final List<? extends VariableElement> params = ((ExecutableElement)e).getParameters();
if(params.isEmpty())
defaultConstructor = true;
@ -134,7 +135,8 @@ public class CompileTimeRowToObjectMapper {
_fieldOrder = null; // didn't successfully finish
this.resultSetConstructor = resultSetConstructor;
if(!resultSetConstructor && !defaultConstructor && !paramConstructor && _columnCount > 2 && componentType == null)
throw new MapperException("Exception when trying to get constructor for : "+_returnTypeClass.toString() + " Must have default no-arg constructor or one that takes a single ResultSet.");
throw new MapperException("Exception when trying to get constructor for : "+_returnTypeClass.toString() + " Must have default no-arg constructor, one that takes a single ResultSet, or one that takes parameters named like the query columns (in any order):\n" +
RowToObjectMapper.keysToString(keys)+ "\nRequires compilation with -parameters argument if source isn't being compiled this pass, beware Bug ID: JDK-8191074 with jdk8, fixed in 9+");
}
}

View File

@ -15,6 +15,7 @@ import javax.tools.Diagnostic;
import java.io.*;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import java.util.regex.Matcher;
@ -65,7 +66,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
return messager;
}
static TypeMirror sqlExceptionType, stringType, numberType, utilDateType, readerType, clobType, jdbcMapperType,
static TypeMirror sqlExceptionType, stringType, numberType, utilDateType, readerType, clobType, connectionType, jdbcMapperType,
byteArrayType, inputStreamType, fileType, blobType, sqlArrayType, collectionType, calendarType, cleanerType, enumType;
//IFJAVA8_START
static TypeMirror instantType, localDateTimeType, localDateType, localTimeType, zonedDateTimeType, offsetDateTimeType, offsetTimeType;
@ -97,6 +98,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
utilDateType = elements.getTypeElement(java.util.Date.class.getCanonicalName()).asType();
readerType = elements.getTypeElement(Reader.class.getCanonicalName()).asType();
clobType = elements.getTypeElement(Clob.class.getCanonicalName()).asType();
connectionType = elements.getTypeElement(Connection.class.getCanonicalName()).asType();
jdbcMapperType = elements.getTypeElement(JdbcMapper.class.getCanonicalName()).asType();
inputStreamType = elements.getTypeElement(InputStream.class.getCanonicalName()).asType();
fileType = elements.getTypeElement(File.class.getCanonicalName()).asType();
@ -234,10 +236,19 @@ public class JdbcMapperProcessor extends AbstractProcessor {
w.write(className);
w.write("() throws SQLException {\n\t\tthis(_conFactory);\n\t}\n");
}
w.write("\n\tprivate ");
w.write(className);
w.write("(final Connection conn, final boolean closeConn) {\n");
if(hasSuperConstructorTakesConnection(genClass))
w.write("\t\tsuper(conn);\n");
w.write("\t\tthis.conn = conn;\n\t\tthis.closeConn = closeConn;\n\t\tif (this.conn == null)\n" +
"\t\t\tthrow new NullPointerException(\"Connection needs to be non-null for JdbcMapper...\");\n\t}\n"
);
w.write("\n\tpublic ");
w.write(className);
w.write("(Connection conn) {\n\t\tthis.conn = conn;\n\t\tthis.closeConn = false;\n\t\tif (this.conn == null)\n" +
"\t\t\tthrow new NullPointerException(\"Connection needs to be non-null for JdbcMapper...\");\n\t}\n" +
w.write("(Connection conn) {\n\t\tthis(conn, false);\n\t}\n" +
"\n\tpublic Connection getConnection() {\n\t\treturn this.conn;\n\t}\n"
);
@ -541,8 +552,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
// and we can create constructors that set closeConn to true!
w.write("\n\tpublic ");
w.write(className);
w.write("(final Factory<Connection> connectionFactory) throws SQLException {\n\t\tthis.conn = connectionFactory.create();\n\t\tthis.closeConn = true;\n\t\tif (this.conn == null)\n" +
"\t\t\tthrow new NullPointerException(\"Connection needs to be non-null for JdbcMapper...\");\n\t}\n"
w.write("(final Factory<Connection> connectionFactory) throws SQLException {\n\t\tthis(connectionFactory.create(), true);\n\t}\n"
);
w.write("\n\tpublic ");
@ -950,4 +960,16 @@ public class JdbcMapperProcessor extends AbstractProcessor {
tryClose(pw);
}
}
public boolean hasSuperConstructorTakesConnection(final TypeElement genClass) {
final List<? extends Element> methodsAndConstructors = genClass.getEnclosedElements();
for(final Element e : methodsAndConstructors) {
if (e.getKind() == ElementKind.CONSTRUCTOR && !e.getModifiers().contains(Modifier.PRIVATE)) {
final List<? extends VariableElement> params = ((ExecutableElement)e).getParameters();
if(params.size() == 1 && types.isSameType(params.get(0).asType(), connectionType))
return true;
}
}
return false;
}
}

View File

@ -197,7 +197,11 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
// 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);
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"
//IFJAVA8_START
+ ", or one that takes parameters named like the query columns (in any order):\n" + keysToString(keys) + "\nRequires compilation with -parameters argument, beware Bug ID: JDK-8191074 with jdk8, fixed in 9+"
//IFJAVA8_END
, e1);
}
}
}
@ -208,6 +212,24 @@ public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
this.constructorLoaded = true;
}
public static String keysToString(final String[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == 0)
return "[]";
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 1; ; i++) {
b.append(String.valueOf(a[i]));
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
/**
* This returns a new instance of the Map class required by Map<String, Object>[]
* It lives in it's own method to minimize suppressed warnings and allow subclasses to override methods,

View File

@ -118,5 +118,6 @@ try(QueryMapper qm = new QueryMapper("java:/comp/env/jdbc/testPool", new ResultS
TODO
----
* Binding of Enum to String by default
* DOCUMENTATION!!!!!
* sql other than select return boolean, int > 0 ?
* @RunInTransaction void support