From f68fd749e39b060f13504786619c2cd1174ed0f7 Mon Sep 17 00:00:00 2001 From: moparisthebest Date: Sat, 17 Mar 2018 00:42:22 -0400 Subject: [PATCH] Call super(Connection) if it exists, tweak cannot find Constructor error message --- .../codegen/CompileTimeRowToObjectMapper.java | 6 ++-- .../jdbc/codegen/JdbcMapperProcessor.java | 32 ++++++++++++++++--- .../jdbc/RowToObjectMapper.java | 24 +++++++++++++- readme.md | 5 +-- 4 files changed, 57 insertions(+), 10 deletions(-) diff --git a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/CompileTimeRowToObjectMapper.java b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/CompileTimeRowToObjectMapper.java index 2aae506..9ddf5dd 100644 --- a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/CompileTimeRowToObjectMapper.java +++ b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/CompileTimeRowToObjectMapper.java @@ -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 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+"); } } diff --git a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java index 73ffbd9..500aeea 100644 --- a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java +++ b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java @@ -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 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 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 methodsAndConstructors = genClass.getEnclosedElements(); + for(final Element e : methodsAndConstructors) { + if (e.getKind() == ElementKind.CONSTRUCTOR && !e.getModifiers().contains(Modifier.PRIVATE)) { + final List params = ((ExecutableElement)e).getParameters(); + if(params.size() == 1 && types.isSameType(params.get(0).asType(), connectionType)) + return true; + } + } + return false; + } } diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java index 6a1e7db..cc74c9c 100644 --- a/querymapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java +++ b/querymapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java @@ -197,7 +197,11 @@ public class RowToObjectMapper extends AbstractRowMapper { // 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 extends AbstractRowMapper { 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[] * It lives in it's own method to minimize suppressed warnings and allow subclasses to override methods, diff --git a/readme.md b/readme.md index 420cbb6..1cbb498 100644 --- a/readme.md +++ b/readme.md @@ -118,5 +118,6 @@ try(QueryMapper qm = new QueryMapper("java:/comp/env/jdbc/testPool", new ResultS TODO ---- - * Binding of Enum to String by default - * sql other than select return boolean, int > 0 ? \ No newline at end of file + * DOCUMENTATION!!!!! + * sql other than select return boolean, int > 0 ? + * @RunInTransaction void support \ No newline at end of file