diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CompilingResultSetMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CompilingResultSetMapper.java index dd66e5c..3d7f108 100644 --- a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CompilingResultSetMapper.java +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CompilingResultSetMapper.java @@ -7,6 +7,14 @@ import java.util.Calendar; import java.util.HashMap; import java.util.Map; +/** + * This generally follows the contract of ResultSetMapper, with the differences specified in CompilingRowToObjectMapper. + *

+ * This does cache compiled code based on column name/order and DTO being mapped to, so the (rather heavy) + * code generation/compilation/instantiation only happens once for each query/dto, and is very fast on subsequent calls. + * + * @see CompilingRowToObjectMapper + */ public class CompilingResultSetMapper extends ResultSetMapper { protected final Compiler compiler = new Compiler(); diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java index 54e678b..05af8c1 100644 --- a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java @@ -12,7 +12,19 @@ import java.util.Calendar; import java.util.Map; /** - * Created by mopar on 5/16/17. + * Map a ResultSet row to an Object. This mapper generates/compiles/executes java code to perform the mapping. + * + * @author Travis Burtrum (modifications from beehive) + * @author Travis Burtrum + * @see RowToObjectMapper for most details, will document here where this differs + *

+ * Usage differences: + * 1. Reflection can set non-public or final fields directly, direct java code cannot, so DTOs like that will result in + * a compilation and therefore mapping error. + *

+ * Subclass differences: + * 1. Normally a subclass of RowToObjectMapper can overload the getMapImplementation() method to change some behavior, + * @see CaseInsensitiveMapRowToObjectMapper , but that method is never called with this implementation. */ public class CompilingRowToObjectMapper extends RowToObjectMapper { protected final Compiler compiler; @@ -72,12 +84,42 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { } protected String typeFromName(final Class type) { - return type.getName(); // todo: naive for now + if (returnMap || componentType == null) + return type.getName(); + else { + // an array, annoying syntax + final String name = type.getName(); + final char charType = name.charAt(1); + switch (charType) { + case 'L': + return name.substring(2, name.length() - 1) + "[]"; + case 'Z': + return "boolean[]"; + case 'B': + return "byte[]"; + case 'C': + return "char[]"; + case 'D': + return "double[]"; + case 'F': + return "float[]"; + case 'I': + return "int[]"; + case 'J': + return "long[]"; + case 'S': + return "short[]"; + case '[': + default: + throw new MapperException("only supports single dimensional array"); + } + } } - protected String escapeJavaString(final String s) { + protected String escapeMapKeyString(final String s) { // todo: escape key string, newlines, double quotes, backslashes... - return s; + // actually it seems like those wouldn't be valid SQL column names, so we won't bother until we hear different... + return '"' + s + '"'; } // code generation down here @@ -110,19 +152,19 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { if (returnMap) // we want a map try { // todo: does not call getMapImplementation, I think that's fine - java.append("final Map ret = new ").append(tType).append("();\n"); + java.append("final ").append(tType).append(" ret = new ").append(tType).append("();\n"); final ResultSetMetaData md = _resultSet.getMetaData(); final int columnLength = _columnCount + 1; if (componentType != null && componentType != Object.class) { // we want a specific value type int typeId = _tmf.getTypeId(componentType); for (int x = 1; x < columnLength; ++x) { - java.append("ret.put(").append(escapeJavaString(md.getColumnName(x).toLowerCase())).append(", "); + java.append("ret.put(").append(escapeMapKeyString(md.getColumnName(x).toLowerCase())).append(", "); extractColumnValueString(java, x, typeId); java.append(");\n"); } } else // we want a generic object type for (int x = 1; x < columnLength; ++x) - java.append("ret.put(").append(escapeJavaString(md.getColumnName(x).toLowerCase())).append(", rs.getObject(").append(x).append("));\n"); + java.append("ret.put(").append(escapeMapKeyString(md.getColumnName(x).toLowerCase())).append(", rs.getObject(").append(x).append("));\n"); java.append("return ret;\n"); return; } catch (Throwable e) { @@ -132,8 +174,7 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { } else if (componentType != null) // we want an array try { - // todo: array initialization syntax? - java.append("final ").append(tType).append("[] ret = new ").append(tType).append("[").append(_columnCount).append("];\n"); + java.append("final ").append(tType).append(" ret = new ").append(tType.substring(0, tType.length() - 1)).append(_columnCount).append("];\n"); final int typeId = _tmf.getTypeId(componentType); for (int x = 0; x < _columnCount; ) { java.append("ret[").append(x).append("] = "); @@ -141,6 +182,7 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { java.append(";\n"); } java.append("return ret;\n"); + return; } catch (Throwable e) { throw new MapperException(e.getClass().getName() + " when trying to create a " + componentType.getName() + "[] from a ResultSet row, all columns must be of that type", e); @@ -236,7 +278,6 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { * @throws java.sql.SQLException on error. */ protected void extractColumnValueString(final StringBuilder java, final int index, final int resultType) { - // todo: custom boolean switch (resultType) { case TypeMappingsFactory.TYPE_INT: java.append("rs.getInt(").append(index).append(")");