From 6fe563f25ad2f87ee62cf25668c0e3803f129e4a Mon Sep 17 00:00:00 2001 From: moparisthebest Date: Tue, 13 Jun 2017 21:42:32 -0400 Subject: [PATCH] Re-factor TypeMappingsFactory with proper support for Enums --- .../codegen/CompileTimeRowToObjectMapper.java | 47 +-- .../jdbc/codegen/JdbcMapperTest.java | 12 + .../jdbc/codegen/PersonDAO.java | 8 + .../jdbc/codegen/PrestoPersonDAO.java | 8 + .../jdbc/CompilingRowToObjectMapper.java | 57 +-- .../jdbc/RowToObjectMapper.java | 29 +- .../jdbc/TypeMappingsFactory.java | 371 ++++-------------- .../moparisthebest/jdbc/QueryMapperTest.java | 11 + .../moparisthebest/jdbc/dto/EnumPerson.java | 54 +++ .../moparisthebest/jdbc/dto/FirstName.java | 10 + 10 files changed, 233 insertions(+), 374 deletions(-) create mode 100644 querymapper/src/test/java/com/moparisthebest/jdbc/dto/EnumPerson.java create mode 100644 querymapper/src/test/java/com/moparisthebest/jdbc/dto/FirstName.java 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 975153d..89a3c32 100644 --- a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/CompileTimeRowToObjectMapper.java +++ b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/CompileTimeRowToObjectMapper.java @@ -272,10 +272,11 @@ public class CompileTimeRowToObjectMapper { java.append("final ").append(tType).append(" ret = new ").append(tType).append("();\n"); final int columnLength = _columnCount + 1; int typeId = getTypeId(componentType); + final String enumName = componentType.toString(); if (componentType != null && typeId != TypeMappingsFactory.TYPE_UNKNOWN) { // we want a specific value type for (int x = 1; x < columnLength; ++x) { java.append("ret.put(").append(escapeMapKeyString(keys[x]).toLowerCase()).append(", "); - extractColumnValueString(java, x, typeId); + extractColumnValueString(java, x, typeId, enumName); java.append(");\n"); } } else // we want a generic object type @@ -291,9 +292,10 @@ public class CompileTimeRowToObjectMapper { try { java.append("final ").append(tType).append(" ret = new ").append(tType.substring(0, tType.length() - 1)).append(String.valueOf(_columnCount)).append("];\n"); final int typeId = getTypeId(componentType); + final String enumName = componentType.toString(); for (int x = 0; x < _columnCount; ) { java.append("ret[").append(String.valueOf(x)).append("] = "); - extractColumnValueString(java, ++x, typeId); + extractColumnValueString(java, ++x, typeId, enumName); java.append(";\n"); } return; @@ -312,7 +314,7 @@ public class CompileTimeRowToObjectMapper { final int typeId = getTypeId(_returnTypeClass); if (typeId != TypeMappingsFactory.TYPE_UNKNOWN) { java.append("final ").append(tType).append(" ret = "); - extractColumnValueString(java, 1, typeId); + extractColumnValueString(java, 1, typeId, _returnTypeClass.toString()); java.append(";\n"); return; } else { @@ -325,7 +327,7 @@ public class CompileTimeRowToObjectMapper { */ // we could actually pull from first row like above and test it first and fail now, but maybe just failing during compilation is enough? java.append("final ").append(tType).append(" ret = (").append(tType).append(") "); - extractColumnValueString(java, 1, typeId); + extractColumnValueString(java, 1, typeId, _returnTypeClass.toString()); java.append(";\n"); return; } @@ -343,36 +345,22 @@ public class CompileTimeRowToObjectMapper { final Element f = _fields[i]; final boolean isField = f.getKind() == ElementKind.FIELD; - //_args[0] = extractColumnValue(i, _fieldTypes[i]); - //System.out.printf("field: '%s' obj: '%s' fieldType: '%s'\n", _fields[i], _args[0], _fieldTypes[i]); - // custom hacked-in support for enums, can do better when we scrap org.apache.beehive.controls.system.jdbc.TypeMappingsFactory - if (_fieldTypes[i] == 0) { - /* - final Class fieldType = isField ? ((Field) f).getType() : ((Method) f).getParameterTypes()[0]; - if (Enum.class.isAssignableFrom(fieldType)) { - _args[0] = Enum.valueOf((Class) fieldType, (String) _args[0]); - if (f instanceof Field) { - // if f not accessible (but super.getFieldMappings() sets it), throw exception during compilation is fine - java.append("ret.").append(((Field) f).getName()).append(" = ").append(typeFromName(fieldType)).append(".valueOf("); - extractColumnValueString(java, i, _fieldTypes[i]); - java.append(");\n"); - } else { - java.append("ret.").append(((Method) f).getName()).append("(").append(typeFromName(fieldType)).append(".valueOf("); - extractColumnValueString(java, i, _fieldTypes[i]); - java.append("));\n"); - } + String enumName = null; + if (_fieldTypes[i] == TypeMappingsFactory.TYPE_ENUM) { + if (f.getKind() == ElementKind.FIELD) { + enumName = ((VariableElement) f).asType().toString(); + } else { + enumName = ((ExecutableElement) f).getParameters().get(0).asType().toString(); } - */ - // no for now... } if (isField) { // if f not accessible (but super.getFieldMappings() sets it), throw exception during compilation is fine java.append("ret.").append(f.getSimpleName().toString()).append(" = "); - extractColumnValueString(java, i, _fieldTypes[i]); + extractColumnValueString(java, i, _fieldTypes[i], enumName); java.append(";\n"); } else { java.append("ret.").append(f.getSimpleName().toString()).append("("); - extractColumnValueString(java, i, _fieldTypes[i]); + extractColumnValueString(java, i, _fieldTypes[i], enumName); java.append(");\n"); } } @@ -385,15 +373,16 @@ public class CompileTimeRowToObjectMapper { try { return _tmf.getTypeId(typeMirrorToClass(classType)); } catch (ClassNotFoundException e) { + // todo: what about enums? return TypeMappingsFactory.TYPE_UNKNOWN; } } - public void extractColumnValueString(final Appendable java, final int index, final int resultType) throws IOException, ClassNotFoundException { - CompilingRowToObjectMapper.extractColumnValueString(java, index, resultType, _resultSetName, _calendarName); + public void extractColumnValueString(final Appendable java, final int index, final int resultType, final String enumName) throws IOException, ClassNotFoundException { + CompilingRowToObjectMapper.extractColumnValueString(java, index, resultType, enumName, _resultSetName, _calendarName); } public void extractColumnValueString(final Appendable java, final int index, final TypeMirror resultType) throws IOException, ClassNotFoundException { - CompilingRowToObjectMapper.extractColumnValueString(java, index, typeMirrorToClass(resultType), _resultSetName, _calendarName); + CompilingRowToObjectMapper.extractColumnValueString(java, index, getTypeId(resultType), resultType.toString(), _resultSetName, _calendarName); } } diff --git a/jdbcmapper/src/test/java/com/moparisthebest/jdbc/codegen/JdbcMapperTest.java b/jdbcmapper/src/test/java/com/moparisthebest/jdbc/codegen/JdbcMapperTest.java index 4e04d53..3505f91 100644 --- a/jdbcmapper/src/test/java/com/moparisthebest/jdbc/codegen/JdbcMapperTest.java +++ b/jdbcmapper/src/test/java/com/moparisthebest/jdbc/codegen/JdbcMapperTest.java @@ -1,6 +1,8 @@ package com.moparisthebest.jdbc.codegen; +import com.moparisthebest.jdbc.dto.EnumPerson; import com.moparisthebest.jdbc.dto.FieldPerson; +import com.moparisthebest.jdbc.dto.FirstName; import com.moparisthebest.jdbc.util.ResultSetIterable; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -91,4 +93,14 @@ public class JdbcMapperTest { } //IFJAVA8_END + + @Test + public void testEnumPerson() throws SQLException { + assertEquals(new EnumPerson(FirstName.First), dao.getEnumPerson(fieldPerson1.getPersonNo())); + } + + @Test + public void testEnum() throws SQLException { + assertEquals(FirstName.First, dao.getFirstNameEnum(fieldPerson1.getPersonNo())); + } } diff --git a/jdbcmapper/src/test/java/com/moparisthebest/jdbc/codegen/PersonDAO.java b/jdbcmapper/src/test/java/com/moparisthebest/jdbc/codegen/PersonDAO.java index ba492d7..31ca9d5 100644 --- a/jdbcmapper/src/test/java/com/moparisthebest/jdbc/codegen/PersonDAO.java +++ b/jdbcmapper/src/test/java/com/moparisthebest/jdbc/codegen/PersonDAO.java @@ -1,7 +1,9 @@ package com.moparisthebest.jdbc.codegen; import com.moparisthebest.jdbc.Cleaner; +import com.moparisthebest.jdbc.dto.EnumPerson; import com.moparisthebest.jdbc.dto.FieldPerson; +import com.moparisthebest.jdbc.dto.FirstName; import com.moparisthebest.jdbc.dto.Person; import com.moparisthebest.jdbc.util.ResultSetIterable; @@ -157,4 +159,10 @@ public interface PersonDAO extends Closeable { Stream getPeopleStreamCachedPreparedStatement(long personNo1, long personNo2, long personNo3) throws SQLException; //IFJAVA8_END + + @JdbcMapper.SQL("SELECT first_name, last_name FROM person WHERE person_no = {personNo}") + EnumPerson getEnumPerson(long personNo) throws SQLException; + + @JdbcMapper.SQL("SELECT first_name FROM person WHERE person_no = {personNo}") + FirstName getFirstNameEnum(long personNo) throws SQLException; } diff --git a/presto-sqlparser/src/test/java/com/moparisthebest/jdbc/codegen/PrestoPersonDAO.java b/presto-sqlparser/src/test/java/com/moparisthebest/jdbc/codegen/PrestoPersonDAO.java index 0c04cbd..1603537 100644 --- a/presto-sqlparser/src/test/java/com/moparisthebest/jdbc/codegen/PrestoPersonDAO.java +++ b/presto-sqlparser/src/test/java/com/moparisthebest/jdbc/codegen/PrestoPersonDAO.java @@ -1,7 +1,9 @@ package com.moparisthebest.jdbc.codegen; import com.moparisthebest.jdbc.Cleaner; +import com.moparisthebest.jdbc.dto.EnumPerson; import com.moparisthebest.jdbc.dto.FieldPerson; +import com.moparisthebest.jdbc.dto.FirstName; import com.moparisthebest.jdbc.dto.Person; import com.moparisthebest.jdbc.util.ResultSetIterable; @@ -157,4 +159,10 @@ public interface PrestoPersonDAO extends PersonDAO { Stream getPeopleStreamCachedPreparedStatement(long personNo1, long personNo2, long personNo3) throws SQLException; //IFJAVA8_END + + @JdbcMapper.SQL("SELECT first_name, last_name FROM person WHERE person_no = {personNo}") + EnumPerson getEnumPerson(long personNo) throws SQLException; + + @JdbcMapper.SQL("SELECT first_name FROM person WHERE person_no = {personNo}") + FirstName getFirstNameEnum(long personNo) throws SQLException; } diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java index 5785164..b9be6f4 100644 --- a/querymapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java +++ b/querymapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java @@ -194,7 +194,7 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { if(mapOnlySecondColumn){ java.append("final ").append(tType).append(" ret = "); - extractColumnValueString(java, 2, _tmf.getTypeId(_returnTypeClass)); + extractColumnValueString(java, 2, _returnTypeClass); java.append(";\n"); return; } @@ -212,9 +212,10 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { final int columnLength = _columnCount + 1; if (componentType != null && componentType != Object.class) { // we want a specific value type int typeId = _tmf.getTypeId(componentType); + final String enumName = componentType.getCanonicalName(); for (int x = 1; x < columnLength; ++x) { java.append("ret.put(").append(escapeMapKeyString(keys[x]).toLowerCase()).append(", "); - extractColumnValueString(java, x, typeId); + extractColumnValueString(java, x, typeId, enumName); java.append(");\n"); } } else // we want a generic object type @@ -230,9 +231,10 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { try { java.append("final ").append(tType).append(" ret = new ").append(tType.substring(0, tType.length() - 1)).append(String.valueOf(_columnCount)).append("];\n"); final int typeId = _tmf.getTypeId(componentType); + final String enumName = componentType.getCanonicalName(); for (int x = 0; x < _columnCount; ) { java.append("ret[").append(String.valueOf(x)).append("] = "); - extractColumnValueString(java, ++x, typeId); + extractColumnValueString(java, ++x, typeId, enumName); java.append(";\n"); } return; @@ -251,7 +253,7 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { try { if (typeId != TypeMappingsFactory.TYPE_UNKNOWN) { java.append("final ").append(tType).append(" ret = "); - extractColumnValueString(java, 1, typeId); + extractColumnValueString(java, 1, typeId, _returnTypeClass.getCanonicalName()); java.append(";\n"); return; } else { @@ -264,7 +266,7 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { */ // we could actually pull from first row like above and test it first and fail now, but maybe just failing during compilation is enough? java.append("final ").append(tType).append(" ret = (").append(tType).append(") "); - extractColumnValueString(java, 1, typeId); + extractColumnValueString(java, 1, typeId, _returnTypeClass.getCanonicalName()); java.append(";\n"); return; } @@ -286,33 +288,18 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { for (int i = 1; i < _fields.length; i++) { AccessibleObject f = _fields[i]; - //_args[0] = extractColumnValue(i, _fieldTypes[i]); - //System.out.printf("field: '%s' obj: '%s' fieldType: '%s'\n", _fields[i], _args[0], _fieldTypes[i]); - // custom hacked-in support for enums, can do better when we scrap org.apache.beehive.controls.system.jdbc.TypeMappingsFactory - if (_fieldTypes[i] == 0) { - final Class fieldType = f instanceof Field ? ((Field) f).getType() : ((Method) f).getParameterTypes()[0]; - if (Enum.class.isAssignableFrom(fieldType)) { - _args[0] = Enum.valueOf((Class) fieldType, (String) _args[0]); - if (f instanceof Field) { - // if f not accessible (but super.getFieldMappings() sets it), throw exception during compilation is fine - java.append("ret.").append(((Field) f).getName()).append(" = ").append(typeFromName(fieldType)).append(".valueOf("); - extractColumnValueString(java, i, _fieldTypes[i]); - java.append(");\n"); - } else { - java.append("ret.").append(((Method) f).getName()).append("(").append(typeFromName(fieldType)).append(".valueOf("); - extractColumnValueString(java, i, _fieldTypes[i]); - java.append("));\n"); - } - } + String enumName = null; + if (_fieldTypes[i] == TypeMappingsFactory.TYPE_ENUM) { + enumName = (f instanceof Field ? ((Field) f).getType() : ((Method) f).getParameterTypes()[0]).getCanonicalName(); } if (f instanceof Field) { // 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]); + extractColumnValueString(java, i, _fieldTypes[i], enumName); java.append(";\n"); } else { java.append("ret.").append(((Method) f).getName()).append("("); - extractColumnValueString(java, i, _fieldTypes[i]); + extractColumnValueString(java, i, _fieldTypes[i], enumName); java.append(");\n"); } } @@ -321,8 +308,8 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { java.append("ret.finish(rs);\n"); } - public void extractColumnValueString(final Appendable java, final int index, final int resultType) throws IOException { - extractColumnValueString(java, index, resultType, _calendarName); + public void extractColumnValueString(final Appendable java, final int index, final int resultType, final String enumName) throws IOException { + extractColumnValueString(java, index, resultType, enumName, _calendarName); } public void extractColumnValueString(final Appendable java, final int index, final Class resultType) throws IOException { @@ -354,16 +341,11 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { } public static void extractColumnValueString(final Appendable java, final int index, final Class resultType, final String calendarName) throws IOException { - extractColumnValueString(java, index, _tmf.getTypeId(resultType), "rs", calendarName); + extractColumnValueString(java, index, _tmf.getTypeId(resultType), resultType.getCanonicalName(), "rs", calendarName); } - public static void extractColumnValueString(final Appendable java, final int index, final Class resultType, final String resultSetName, final String calendarName) throws IOException { - extractColumnValueString(java, index, _tmf.getTypeId(resultType), resultSetName, calendarName); - } - - - public static void extractColumnValueString(final Appendable java, final int index, final int resultType, final String calendarName) throws IOException { - extractColumnValueString(java, index, resultType, "rs", calendarName); + public static void extractColumnValueString(final Appendable java, final int index, final int resultType, final String enumName, final String calendarName) throws IOException { + extractColumnValueString(java, index, resultType, enumName, "rs", calendarName); } /** @@ -374,7 +356,7 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { * @return The extracted value * @throws java.sql.SQLException on error. */ - public static void extractColumnValueString(final Appendable java, final int index, final int resultType, final String resultSetName, final String calendarName) throws IOException { + public static void extractColumnValueString(final Appendable java, final int index, final int resultType, final String enumName, final String resultSetName, final String calendarName) throws IOException { switch (resultType) { case TypeMappingsFactory.TYPE_INT: java.append(resultSetName).append(".getInt(").append(String.valueOf(index)).append(")"); @@ -422,6 +404,9 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { case TypeMappingsFactory.TYPE_XMLBEAN_ENUM: java.append(resultSetName).append(".getString(").append(String.valueOf(index)).append(")"); return; + case TypeMappingsFactory.TYPE_ENUM: + java.append(enumName).append(".valueOf(").append(resultSetName).append(".getString(").append(String.valueOf(index)).append("))"); + return; case TypeMappingsFactory.TYPE_BIG_DECIMAL: java.append(resultSetName).append(".getBigDecimal(").append(String.valueOf(index)).append(")"); return; diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java index 77d81b5..e6c944b 100644 --- a/querymapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java +++ b/querymapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java @@ -198,7 +198,7 @@ public class RowToObjectMapper extends AbstractRowMapper { if(componentType != null && componentType != Object.class){ // we want a specific value type int typeId = _tmf.getTypeId(componentType); for(int x = 1; x < columnLength; ++x) - ret.put(keys[x].toLowerCase(), extractColumnValue(x, typeId)); + ret.put(keys[x].toLowerCase(), extractColumnValue(x, typeId, componentType)); } else // we want a generic object type for(int x = 1; x < columnLength; ++x) ret.put(keys[x].toLowerCase(), _resultSet.getObject(x)); @@ -213,7 +213,7 @@ public class RowToObjectMapper extends AbstractRowMapper { final Object ret = Array.newInstance(componentType, _columnCount); final int typeId = _tmf.getTypeId(componentType); for(int x = 0; x < _columnCount;) - Array.set(ret, x, extractColumnValue(++x, typeId)); + Array.set(ret, x, extractColumnValue(++x, typeId, componentType)); //ret[x] = extractColumnValue(++x, typeId); return _returnTypeClass.cast(ret); } catch (Throwable e) { @@ -233,10 +233,10 @@ public class RowToObjectMapper extends AbstractRowMapper { try { if (typeId != TypeMappingsFactory.TYPE_UNKNOWN) { - return (T)extractColumnValue(1, typeId); + return (T)extractColumnValue(1, typeId, _returnTypeClass); } else { // we still might want a single value (i.e. java.util.Date) - Object val = extractColumnValue(1, typeId); + Object val = extractColumnValue(1, typeId, _returnTypeClass); if (_returnTypeClass.isAssignableFrom(val.getClass())) { return _returnTypeClass.cast(val); } @@ -267,14 +267,8 @@ public class RowToObjectMapper extends AbstractRowMapper { AccessibleObject f = _fields[i]; try { - _args[0] = extractColumnValue(i, _fieldTypes[i]); + _args[0] = extractColumnValue(i, _fieldTypes[i], null); // be lazy about this //System.out.printf("field: '%s' obj: '%s' fieldType: '%s'\n", _fields[i], _args[0], _fieldTypes[i]); - // custom hacked-in support for enums, can do better when we scrap org.apache.beehive.controls.system.jdbc.TypeMappingsFactory - if(_fieldTypes[i] == 0 && _args[0] instanceof String){ - Class fieldType = f instanceof Field ? ((Field)f).getType() : ((Method)f).getParameterTypes()[0]; - if(Enum.class.isAssignableFrom(fieldType)) - _args[0] = Enum.valueOf((Class)fieldType, (String)_args[0]); - } if (f instanceof Field) { ((Field) f).set(resultObject, _args[0]); } else { @@ -333,7 +327,7 @@ public class RowToObjectMapper extends AbstractRowMapper { */ @SuppressWarnings({"unchecked"}) public E extractColumnValue(int index, Class classType) throws SQLException { - return classType.cast(extractColumnValue(index, _tmf.getTypeId(classType))); + return classType.cast(extractColumnValue(index, _tmf.getTypeId(classType), classType)); } /** @@ -499,7 +493,7 @@ public class RowToObjectMapper extends AbstractRowMapper { * @return The extracted value * @throws java.sql.SQLException on error. */ - protected Object extractColumnValue(int index, int resultType) throws SQLException { + protected Object extractColumnValue(final int index, final int resultType, Class resultTypeClass) throws SQLException { try{ switch (resultType) { case TypeMappingsFactory.TYPE_INT: @@ -572,6 +566,15 @@ public class RowToObjectMapper extends AbstractRowMapper { case TypeMappingsFactory.TYPE_STRING: 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)resultTypeClass, _resultSet.getString(index)); + return ret; case TypeMappingsFactory.TYPE_BIG_DECIMAL: return _resultSet.getBigDecimal(index); case TypeMappingsFactory.TYPE_BYTES: diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/TypeMappingsFactory.java b/querymapper/src/main/java/com/moparisthebest/jdbc/TypeMappingsFactory.java index 5956922..3f2ab34 100644 --- a/querymapper/src/main/java/com/moparisthebest/jdbc/TypeMappingsFactory.java +++ b/querymapper/src/main/java/com/moparisthebest/jdbc/TypeMappingsFactory.java @@ -18,11 +18,8 @@ */ package com.moparisthebest.jdbc; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.sql.Blob; import java.sql.Clob; -import java.sql.Types; import java.util.HashMap; import java.util.Map; @@ -31,9 +28,7 @@ import java.util.Map; */ public final class TypeMappingsFactory { - /* @todo: refactor! */ - - private static TypeMappingsFactory _instance; + private final static TypeMappingsFactory _instance = new TypeMappingsFactory(); private static Class XMLBEANS_STRING_ENUM_ABSTRACT_BASE = null; static { @@ -49,259 +44,103 @@ public final class TypeMappingsFactory { * @return TypeMappingsFactory instance. */ public static TypeMappingsFactory getInstance() { - if (_instance == null) { - _instance = new TypeMappingsFactory(); - } return _instance; } public static final int TYPE_UNKNOWN = 0; - static final int TYPE_BYTE = 1; - static final int TYPE_SHORT = 2; - static final int TYPE_INT = 3; - static final int TYPE_LONG = 4; - static final int TYPE_FLOAT = 5; - static final int TYPE_DOUBLE = 6; - static final int TYPE_BOOLEAN = 7; - static final int TYPE_BYTE_OBJ = 8; - static final int TYPE_SHORT_OBJ = 9; - static final int TYPE_INT_OBJ = 10; - static final int TYPE_LONG_OBJ = 11; - static final int TYPE_FLOAT_OBJ = 12; - static final int TYPE_DOUBLE_OBJ = 13; - static final int TYPE_BOOLEAN_OBJ = 14; - static final int TYPE_BIG_DECIMAL = 15; - static final int TYPE_STRING = 16; - static final int TYPE_BYTES = 17; - static final int TYPE_SQLDATE = 18; - static final int TYPE_TIME = 19; - static final int TYPE_TIMESTAMP = 20; - static final int TYPE_STREAM = 21; - static final int TYPE_READER = 22; - static final int TYPE_CLOB = 23; - static final int TYPE_BLOB = 24; - static final int TYPE_ARRAY = 25; - static final int TYPE_REF = 26; - static final int TYPE_DATE = 27; - static final int TYPE_CALENDAR = 28; - static final int TYPE_STRUCT = 29; - static final int TYPE_XMLBEAN_ENUM = 30; - static final int TYPE_MAX = 31; + public static final int TYPE_BYTE = 1; + public static final int TYPE_SHORT = 2; + public static final int TYPE_INT = 3; + public static final int TYPE_LONG = 4; + public static final int TYPE_FLOAT = 5; + public static final int TYPE_DOUBLE = 6; + public static final int TYPE_BOOLEAN = 7; + public static final int TYPE_BYTE_OBJ = 8; + public static final int TYPE_SHORT_OBJ = 9; + public static final int TYPE_INT_OBJ = 10; + public static final int TYPE_LONG_OBJ = 11; + public static final int TYPE_FLOAT_OBJ = 12; + public static final int TYPE_DOUBLE_OBJ = 13; + public static final int TYPE_BOOLEAN_OBJ = 14; + public static final int TYPE_BIG_DECIMAL = 15; + public static final int TYPE_STRING = 16; + public static final int TYPE_BYTES = 17; + public static final int TYPE_SQLDATE = 18; + public static final int TYPE_TIME = 19; + public static final int TYPE_TIMESTAMP = 20; + public static final int TYPE_STREAM = 21; + public static final int TYPE_READER = 22; + public static final int TYPE_CLOB = 23; + public static final int TYPE_BLOB = 24; + public static final int TYPE_ARRAY = 25; + public static final int TYPE_REF = 26; + public static final int TYPE_DATE = 27; + public static final int TYPE_CALENDAR = 28; + public static final int TYPE_STRUCT = 29; + public static final int TYPE_XMLBEAN_ENUM = 30; + public static final int TYPE_ENUM = 31; + private static final int TYPE_MAX = TYPE_ENUM + 1; // should always reference the max - private Map _primitiveDefaults; + private final Map _primitiveDefaults; // // keys in this map are the class of the method's return type, // values are the set of constants defined above all prefixed with // TYPE_ // - private Map _typeMap; - private Map _typeSqlMap; - - /** - * Map a string version of sql type to sql type (java.sql.Types). - * example: "INTEGER" maps to java.sql.Types.INTEGER - */ - private Map _typeSqlNameMap; - - private static Method _methodMapGet; + private final Map _typeMap; /** * Constructor */ - TypeMappingsFactory() { + protected TypeMappingsFactory() { _primitiveDefaults = new HashMap(); _primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE); - _primitiveDefaults.put(Integer.TYPE, new Integer(0)); - _primitiveDefaults.put(Long.TYPE, new Long(0)); - _primitiveDefaults.put(Byte.TYPE, new Byte((byte) 0)); - _primitiveDefaults.put(Short.TYPE, new Short((short) 0)); - _primitiveDefaults.put(Character.TYPE, new Character('\u0000')); - _primitiveDefaults.put(Float.TYPE, new Float(0.0f)); - _primitiveDefaults.put(Double.TYPE, new Double(0.0d)); + _primitiveDefaults.put(Integer.TYPE, 0); + _primitiveDefaults.put(Long.TYPE, 0); + _primitiveDefaults.put(Byte.TYPE, (byte) 0); + _primitiveDefaults.put(Short.TYPE, (short) 0); + _primitiveDefaults.put(Character.TYPE, '\u0000'); + _primitiveDefaults.put(Float.TYPE, 0.0f); + _primitiveDefaults.put(Double.TYPE, 0.0d); // Class to internal enum _typeMap = new HashMap(TYPE_MAX * 2); - _typeMap.put(Boolean.TYPE, new Integer(TYPE_BOOLEAN)); - _typeMap.put(Integer.TYPE, new Integer(TYPE_INT)); - _typeMap.put(Long.TYPE, new Integer(TYPE_LONG)); - _typeMap.put(Byte.TYPE, new Integer(TYPE_BYTE)); - _typeMap.put(Short.TYPE, new Integer(TYPE_SHORT)); - _typeMap.put(Float.TYPE, new Integer(TYPE_FLOAT)); - _typeMap.put(Double.TYPE, new Integer(TYPE_DOUBLE)); - _typeMap.put(Boolean.class, new Integer(TYPE_BOOLEAN_OBJ)); - _typeMap.put(Integer.class, new Integer(TYPE_INT_OBJ)); - _typeMap.put(Long.class, new Integer(TYPE_LONG_OBJ)); - _typeMap.put(Byte.class, new Integer(TYPE_BYTE_OBJ)); - _typeMap.put(Short.class, new Integer(TYPE_SHORT_OBJ)); - _typeMap.put(Float.class, new Integer(TYPE_FLOAT_OBJ)); - _typeMap.put(Double.class, new Integer(TYPE_DOUBLE_OBJ)); - _typeMap.put(String.class, new Integer(TYPE_STRING)); - _typeMap.put(java.math.BigDecimal.class, new Integer(TYPE_BIG_DECIMAL)); - _typeMap.put(byte[].class, new Integer(TYPE_BYTES)); - _typeMap.put(java.sql.Timestamp.class, new Integer(TYPE_TIMESTAMP)); - _typeMap.put(java.sql.Time.class, new Integer(TYPE_TIME)); - _typeMap.put(java.sql.Date.class, new Integer(TYPE_SQLDATE)); - _typeMap.put(java.sql.Ref.class, new Integer(TYPE_REF)); - _typeMap.put(Blob.class, new Integer(TYPE_BLOB)); - _typeMap.put(Clob.class, new Integer(TYPE_CLOB)); - _typeMap.put(java.sql.Array.class, new Integer(TYPE_ARRAY)); - _typeMap.put(java.sql.Struct.class, new Integer(TYPE_STRUCT)); - _typeMap.put(java.io.Reader.class, new Integer(TYPE_READER)); - _typeMap.put(java.io.InputStream.class, new Integer(TYPE_STREAM)); - _typeMap.put(java.util.Date.class, new Integer(TYPE_DATE)); - _typeMap.put(java.util.Calendar.class, new Integer(TYPE_CALENDAR)); - _typeMap.put(java.util.GregorianCalendar.class, new Integer(TYPE_CALENDAR)); + _typeMap.put(Boolean.TYPE, TYPE_BOOLEAN); + _typeMap.put(Integer.TYPE, TYPE_INT); + _typeMap.put(Long.TYPE, TYPE_LONG); + _typeMap.put(Byte.TYPE, TYPE_BYTE); + _typeMap.put(Short.TYPE, TYPE_SHORT); + _typeMap.put(Float.TYPE, TYPE_FLOAT); + _typeMap.put(Double.TYPE, TYPE_DOUBLE); + _typeMap.put(Boolean.class, TYPE_BOOLEAN_OBJ); + _typeMap.put(Integer.class, TYPE_INT_OBJ); + _typeMap.put(Long.class, TYPE_LONG_OBJ); + _typeMap.put(Byte.class, TYPE_BYTE_OBJ); + _typeMap.put(Short.class, TYPE_SHORT_OBJ); + _typeMap.put(Float.class, TYPE_FLOAT_OBJ); + _typeMap.put(Double.class, TYPE_DOUBLE_OBJ); + _typeMap.put(String.class, TYPE_STRING); + _typeMap.put(java.math.BigDecimal.class, TYPE_BIG_DECIMAL); + _typeMap.put(byte[].class, TYPE_BYTES); + _typeMap.put(java.sql.Timestamp.class, TYPE_TIMESTAMP); + _typeMap.put(java.sql.Time.class, TYPE_TIME); + _typeMap.put(java.sql.Date.class, TYPE_SQLDATE); + _typeMap.put(java.sql.Ref.class, TYPE_REF); + _typeMap.put(Blob.class, TYPE_BLOB); + _typeMap.put(Clob.class, TYPE_CLOB); + _typeMap.put(java.sql.Array.class, TYPE_ARRAY); + _typeMap.put(java.sql.Struct.class, TYPE_STRUCT); + _typeMap.put(java.io.Reader.class, TYPE_READER); + _typeMap.put(java.io.InputStream.class, TYPE_STREAM); + _typeMap.put(java.util.Date.class, TYPE_DATE); + _typeMap.put(java.util.Calendar.class, TYPE_CALENDAR); + _typeMap.put(java.util.GregorianCalendar.class, TYPE_CALENDAR); if (XMLBEANS_STRING_ENUM_ABSTRACT_BASE != null) { - _typeMap.put(XMLBEANS_STRING_ENUM_ABSTRACT_BASE, new Integer(TYPE_XMLBEAN_ENUM)); + _typeMap.put(XMLBEANS_STRING_ENUM_ABSTRACT_BASE, TYPE_XMLBEAN_ENUM); } - - // Class to java.sql.Types - _typeSqlMap = new HashMap(TYPE_MAX * 2); - _typeSqlMap.put(Boolean.TYPE, new Integer(Types.BOOLEAN)); - _typeSqlMap.put(Integer.TYPE, new Integer(Types.INTEGER)); - _typeSqlMap.put(Long.TYPE, new Integer(Types.BIGINT)); - _typeSqlMap.put(Byte.TYPE, new Integer(Types.TINYINT)); - _typeSqlMap.put(Short.TYPE, new Integer(Types.SMALLINT)); - _typeSqlMap.put(Float.TYPE, new Integer(Types.REAL)); - _typeSqlMap.put(Double.TYPE, new Integer(Types.DOUBLE)); - _typeSqlMap.put(Boolean.class, new Integer(Types.BOOLEAN)); - _typeSqlMap.put(Integer.class, new Integer(Types.INTEGER)); - _typeSqlMap.put(Long.class, new Integer(Types.BIGINT)); - _typeSqlMap.put(Byte.class, new Integer(Types.TINYINT)); - _typeSqlMap.put(Short.class, new Integer(Types.SMALLINT)); - _typeSqlMap.put(Float.class, new Integer(Types.REAL)); - _typeSqlMap.put(Double.class, new Integer(Types.DOUBLE)); - _typeSqlMap.put(String.class, new Integer(Types.VARCHAR)); - _typeSqlMap.put(java.math.BigDecimal.class, new Integer(Types.DECIMAL)); - _typeSqlMap.put(byte[].class, new Integer(Types.VARBINARY)); - _typeSqlMap.put(java.sql.Timestamp.class, new Integer(Types.TIMESTAMP)); - _typeSqlMap.put(java.sql.Time.class, new Integer(Types.TIME)); - _typeSqlMap.put(java.sql.Date.class, new Integer(Types.DATE)); - _typeSqlMap.put(java.sql.Ref.class, new Integer(Types.REF)); - _typeSqlMap.put(Blob.class, new Integer(Types.BLOB)); - _typeSqlMap.put(Clob.class, new Integer(Types.CLOB)); - _typeSqlMap.put(java.sql.Array.class, new Integer(Types.ARRAY)); - _typeSqlMap.put(java.sql.Struct.class, new Integer(Types.STRUCT)); - _typeSqlMap.put(java.util.Date.class, new Integer(Types.TIMESTAMP)); - _typeSqlMap.put(java.util.Calendar.class, new Integer(Types.TIMESTAMP)); - _typeSqlMap.put(java.util.GregorianCalendar.class, new Integer(Types.TIMESTAMP)); - if (XMLBEANS_STRING_ENUM_ABSTRACT_BASE != null) { - _typeSqlMap.put(XMLBEANS_STRING_ENUM_ABSTRACT_BASE, new Integer(Types.VARCHAR)); - } - - // String to java.sql.Types - _typeSqlNameMap = new HashMap(TYPE_MAX * 2); - _typeSqlNameMap.put("BIT", new Integer(Types.BIT)); - _typeSqlNameMap.put("TINYINT", new Integer(Types.TINYINT)); - _typeSqlNameMap.put("SMALLINT", new Integer(Types.SMALLINT)); - _typeSqlNameMap.put("INTEGER", new Integer(Types.INTEGER)); - _typeSqlNameMap.put("BIGINT", new Integer(Types.BIGINT)); - _typeSqlNameMap.put("FLOAT", new Integer(Types.REAL)); - _typeSqlNameMap.put("REAL", new Integer(Types.REAL)); - _typeSqlNameMap.put("DOUBLE", new Integer(Types.DOUBLE)); - _typeSqlNameMap.put("NUMERIC", new Integer(Types.NUMERIC)); - _typeSqlNameMap.put("DECIMAL", new Integer(Types.DECIMAL)); - _typeSqlNameMap.put("CHAR", new Integer(Types.CHAR)); - _typeSqlNameMap.put("VARCHAR", new Integer(Types.VARCHAR)); - _typeSqlNameMap.put("LONGVARCHAR", new Integer(Types.LONGVARCHAR)); - _typeSqlNameMap.put("DATE", new Integer(Types.DATE)); - _typeSqlNameMap.put("TIME", new Integer(Types.TIME)); - _typeSqlNameMap.put("TIMESTAMP", new Integer(Types.TIMESTAMP)); - _typeSqlNameMap.put("BINARY", new Integer(Types.BINARY)); - _typeSqlNameMap.put("VARBINARY", new Integer(Types.VARBINARY)); - _typeSqlNameMap.put("LONGVARBINARY", new Integer(Types.LONGVARBINARY)); - _typeSqlNameMap.put("NULL", new Integer(Types.NULL)); - _typeSqlNameMap.put("OTHER", new Integer(Types.OTHER)); - _typeSqlNameMap.put("JAVA_OBJECT", new Integer(Types.JAVA_OBJECT)); - _typeSqlNameMap.put("DISTINCT", new Integer(Types.DISTINCT)); - _typeSqlNameMap.put("STRUCT", new Integer(Types.STRUCT)); - _typeSqlNameMap.put("ARRAY", new Integer(Types.ARRAY)); - _typeSqlNameMap.put("BLOB", new Integer(Types.BLOB)); - _typeSqlNameMap.put("CLOB", new Integer(Types.CLOB)); - _typeSqlNameMap.put("REF", new Integer(Types.REF)); - _typeSqlNameMap.put("DATALINK", new Integer(Types.DATALINK)); - _typeSqlNameMap.put("BOOLEAN", new Integer(Types.BOOLEAN)); - - // some JAVA synonyms - _typeSqlNameMap.put("BYTE", new Integer(Types.TINYINT)); - _typeSqlNameMap.put("SHORT", new Integer(Types.SMALLINT)); - _typeSqlNameMap.put("INT", new Integer(Types.INTEGER)); - _typeSqlNameMap.put("LONG", new Integer(Types.BIGINT)); - - // cache the Map.get method for efficiency - try { - _methodMapGet = Map.class.getMethod("get", new Class[]{Object.class}); - } catch (NoSuchMethodException e) { - throw new MapperException("Can not find java.util.Map.get(Object) method"); - } - } - - /** - * Convert a type string to its SQL Type int value. - * @param type A String containing the SQL type name. - * @return The SQL type, TYPE_UNKNOWN if cannot convert. - */ - public int convertStringToSQLType(String type) { - if (_typeSqlNameMap.containsKey(type.toUpperCase())) { - return _typeSqlNameMap.get(type.toUpperCase()); - } - return TYPE_UNKNOWN; - } - - /** - * Get the SQL type of a class, start at top level class an check all super classes until match is found. - * @param classType Class to get SQL type of. - * @return Types.OTHER if cannot find SQL type. - */ - public int getSqlType(Class classType) { - - final Class origType = classType; - while (classType != null) { - Integer type = _typeSqlMap.get(classType); - if (type != null) { - return type.intValue(); - } - classType = classType.getSuperclass(); - } - - // - // special check for blobs/clobs they are interfaces not derived from - // - if (Blob.class.isAssignableFrom(origType)) { - return _typeSqlMap.get(Blob.class).intValue(); - } else if (Clob.class.isAssignableFrom(origType)) { - return _typeSqlMap.get(Clob.class).intValue(); - } - - return Types.OTHER; - } - - /** - * Get the SQL type for an object. - * @param o Object to get SQL type of. - * @return SQL type of the object, Types.OTHER if cannot classify. - */ - public int getSqlType(Object o) { - if (null == o) { - return Types.NULL; - } - return getSqlType(o.getClass()); - } - - /** - * - * @param val - * @param args - * @return the type - * @throws IllegalAccessException - * @throws java.lang.reflect.InvocationTargetException - */ - public Object lookupType(Object val, Object[] args) - throws IllegalAccessException, InvocationTargetException - { - return _methodMapGet.invoke(val, args); + _typeMap.put(Enum.class, TYPE_ENUM); } /** @@ -343,64 +182,4 @@ public final class TypeMappingsFactory { public Object fixNull(Class type) { return type.isPrimitive() ? _primitiveDefaults.get(type) : null; } - - /** - * Create an Object array for the given array. - * - * @param o An array. - * @return A new object array. - */ - public static Object[] toObjectArray(Object o) { - - Class clas = o.getClass().getComponentType(); - - if (null == clas) return null; - - Object[] arr; - - if (clas == Boolean.TYPE) { - boolean[] src = (boolean[]) o; - arr = new Object[src.length]; - for (int i = 0; i < src.length; i++) - arr[i] = new Boolean(src[i]); - } else if (clas == Character.TYPE) { - char[] src = (char[]) o; - arr = new Object[src.length]; - for (int i = 0; i < src.length; i++) - arr[i] = new Character(src[i]); - } else if (clas == Byte.TYPE) { - byte[] src = (byte[]) o; - arr = new Object[src.length]; - for (int i = 0; i < src.length; i++) - arr[i] = new Byte(src[i]); - } else if (clas == Short.TYPE) { - short[] src = (short[]) o; - arr = new Object[src.length]; - for (int i = 0; i < src.length; i++) - arr[i] = new Short(src[i]); - } else if (clas == Integer.TYPE) { - int[] src = (int[]) o; - arr = new Object[src.length]; - for (int i = 0; i < src.length; i++) - arr[i] = new Integer(src[i]); - } else if (clas == Long.TYPE) { - long[] src = (long[]) o; - arr = new Object[src.length]; - for (int i = 0; i < src.length; i++) - arr[i] = new Long(src[i]); - } else if (clas == Float.TYPE) { - float[] src = (float[]) o; - arr = new Object[src.length]; - for (int i = 0; i < src.length; i++) - arr[i] = new Float(src[i]); - } else if (clas == Double.TYPE) { - double[] src = (double[]) o; - arr = new Object[src.length]; - for (int i = 0; i < src.length; i++) - arr[i] = new Double(src[i]); - } else { - arr = (Object[]) o; - } - return arr; - } } diff --git a/querymapper/src/test/java/com/moparisthebest/jdbc/QueryMapperTest.java b/querymapper/src/test/java/com/moparisthebest/jdbc/QueryMapperTest.java index fba9ade..9557c9c 100644 --- a/querymapper/src/test/java/com/moparisthebest/jdbc/QueryMapperTest.java +++ b/querymapper/src/test/java/com/moparisthebest/jdbc/QueryMapperTest.java @@ -425,4 +425,15 @@ public class QueryMapperTest { //IFJAVA8_END + + + @Test + public void testEnumPerson() throws SQLException { + assertEquals(new EnumPerson(FirstName.First), qm.toObject("SELECT first_name, last_name FROM person WHERE person_no = ?", EnumPerson.class, fieldPerson1.getPersonNo())); + } + + @Test + public void testEnum() throws SQLException { + assertEquals(FirstName.First, qm.toObject("SELECT first_name FROM person WHERE person_no = ?", FirstName.class, fieldPerson1.getPersonNo())); + } } diff --git a/querymapper/src/test/java/com/moparisthebest/jdbc/dto/EnumPerson.java b/querymapper/src/test/java/com/moparisthebest/jdbc/dto/EnumPerson.java new file mode 100644 index 0000000..839639d --- /dev/null +++ b/querymapper/src/test/java/com/moparisthebest/jdbc/dto/EnumPerson.java @@ -0,0 +1,54 @@ +package com.moparisthebest.jdbc.dto; + +import java.util.Date; + +/** + * Created by mopar on 6/13/17. + */ +public class EnumPerson implements Person { + + public FirstName firstName; + public String lastName; + + public EnumPerson() { + } + + public EnumPerson(final FirstName firstName) { + this.firstName = firstName; + } + + @Override + public long getPersonNo() { + return 0; + } + + @Override + public Date getBirthDate() { + return null; + } + + @Override + public String getFirstName() { + return firstName.name(); + } + + @Override + public String getLastName() { + return lastName; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (!(o instanceof EnumPerson)) return false; + + final EnumPerson that = (EnumPerson) o; + + return firstName == that.firstName; + } + + @Override + public int hashCode() { + return firstName != null ? firstName.hashCode() : 0; + } +} diff --git a/querymapper/src/test/java/com/moparisthebest/jdbc/dto/FirstName.java b/querymapper/src/test/java/com/moparisthebest/jdbc/dto/FirstName.java new file mode 100644 index 0000000..d5209ee --- /dev/null +++ b/querymapper/src/test/java/com/moparisthebest/jdbc/dto/FirstName.java @@ -0,0 +1,10 @@ +package com.moparisthebest.jdbc.dto; + +/** + * Created by mopar on 6/13/17. + */ +public enum FirstName { + First, + Second, + Third, +}