diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/AbstractRowMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/AbstractRowMapper.java new file mode 100644 index 0000000..3a46bb5 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/AbstractRowMapper.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Header:$ + */ + +package com.moparisthebest.jdbc; + +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.Calendar; + +/** + * Abstract base class for all row mappers. + * + * RowMappers are used to map the contents of a row in a ResultSet to the return type of an annotated method. + * Supported RowMapper types include: HashMap, Map, Object, XmlObject. When a ResultSetMapper is ready to + * map a ResultSet row to an object, it requests a RowMapper for the return type of the method from the + * RowMapperFactory. + * + */ +public abstract class AbstractRowMapper implements RowMapper { + + /** ResultSet to map. */ + protected final ResultSet _resultSet; + + /** Calendar instance for date/time mappings. */ + protected final Calendar _cal; + + /** Class to map ResultSet Rows to. */ + protected final Class _returnTypeClass; + + protected final Class _mapKeyType; + + protected final int _columnCount; + + protected final boolean mapOnlySecondColumn; + + /** + * Create a new RowMapper for the specified ResultSet and return type Class. + * @param resultSet ResultSet to map + * @param returnTypeClass Class to map ResultSet rows to. + * @param cal Calendar instance for date/time values. + */ + protected AbstractRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapKeyType) { + _resultSet = resultSet; + _returnTypeClass = returnTypeClass; + _cal = cal; + _mapKeyType = mapKeyType; + + try { + _columnCount = resultSet.getMetaData().getColumnCount(); + } catch (SQLException e) { + throw new MapperException("RowToObjectMapper: SQLException: " + e.getMessage(), e); + } + + mapOnlySecondColumn = _mapKeyType != null && _columnCount == 2; + } + + protected AbstractRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal) { + this(resultSet, returnTypeClass, cal, null); + } + + /** + * Build a String array of column names from the ResultSet. + * @return A String array containing the column names contained within the ResultSet. + * @throws java.sql.SQLException on error + */ + protected String[] getKeysFromResultSet() throws SQLException { + + String[] keys; + final ResultSetMetaData md = _resultSet.getMetaData(); + + keys = new String[_columnCount + 1]; + for (int i = 1; i <= _columnCount; i++) { + keys[i] = md.getColumnName(i).toUpperCase(); + } + return keys; + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CachingResultSetMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CachingResultSetMapper.java index a48a900..9bd4209 100644 --- a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CachingResultSetMapper.java +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CachingResultSetMapper.java @@ -132,7 +132,7 @@ public class CachingResultSetMapper extends ResultSetMapper { } @Override - protected RowToObjectMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { + public RowMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { return new CachingRowToObjectMapper(cache, resultSet, returnTypeClass, cal, mapValType, mapKeyType); } } diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CaseInsensitiveMapResultSetMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CaseInsensitiveMapResultSetMapper.java index e2b57be..cab0e7c 100644 --- a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CaseInsensitiveMapResultSetMapper.java +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CaseInsensitiveMapResultSetMapper.java @@ -15,7 +15,7 @@ public class CaseInsensitiveMapResultSetMapper extends ResultSetMapper { } @Override - protected RowToObjectMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { - return new CaseInsensitiveMapRowToObjectMapper(resultSet, returnTypeClass, cal, mapValType, mapKeyType); + public RowMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { + return new RowToObjectMapper(resultSet, returnTypeClass, cal, mapValType, mapKeyType, true); } } diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CaseInsensitiveMapRowToObjectMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CaseInsensitiveMapRowToObjectMapper.java deleted file mode 100644 index bbe8242..0000000 --- a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CaseInsensitiveMapRowToObjectMapper.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.moparisthebest.jdbc; - -import java.sql.ResultSet; -import java.util.Calendar; -import java.util.HashMap; -import java.util.Map; - -/** - * Created by mopar on 5/15/14. - */ -public class CaseInsensitiveMapRowToObjectMapper extends RowToObjectMapper { - public CaseInsensitiveMapRowToObjectMapper(ResultSet resultSet, Class returnTypeClass) { - super(resultSet, returnTypeClass); - } - - public CaseInsensitiveMapRowToObjectMapper(ResultSet resultSet, Class returnTypeClass, Class mapValType) { - super(resultSet, returnTypeClass, mapValType); - } - - public CaseInsensitiveMapRowToObjectMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal) { - super(resultSet, returnTypeClass, cal); - } - - public CaseInsensitiveMapRowToObjectMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType) { - super(resultSet, returnTypeClass, cal, mapValType); - } - - public CaseInsensitiveMapRowToObjectMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { - super(resultSet, returnTypeClass, cal, mapValType, mapKeyType); - } - - @Override - protected Map getMapImplementation() throws IllegalAccessException, InstantiationException { - if(HashMap.class.equals(_returnTypeClass)) - return new HashMap(){ - @Override - public Object get(Object key) { - return super.get(key instanceof String ? ((String)key).toLowerCase() : key); - } - - @Override - public boolean containsKey(Object key) { - return super.containsKey(key instanceof String ? ((String)key).toLowerCase() : key); - } - }; - return super.getMapImplementation(); - } -} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningCachingResultSetMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningCachingResultSetMapper.java new file mode 100644 index 0000000..23d141f --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningCachingResultSetMapper.java @@ -0,0 +1,68 @@ +package com.moparisthebest.jdbc; + +import java.sql.ResultSet; +import java.util.Calendar; +import java.util.Map; + +/** + * Created by mopar on 5/18/17. + */ +public class CleaningCachingResultSetMapper extends CachingResultSetMapper { + + private final Cleaner cleaner; + + public CleaningCachingResultSetMapper(final Cleaner cleaner, final Calendar cal, final int arrayMaxLength, final int maxEntries) { + super(cal, arrayMaxLength, maxEntries); + this.cleaner = cleaner; + } + + public CleaningCachingResultSetMapper(final Cleaner cleaner, final Calendar cal, final int arrayMaxLength) { + super(cal, arrayMaxLength); + this.cleaner = cleaner; + } + + public CleaningCachingResultSetMapper(final Cleaner cleaner, final int arrayMaxLength) { + super(arrayMaxLength); + this.cleaner = cleaner; + } + + public CleaningCachingResultSetMapper(final Cleaner cleaner) { + this.cleaner = cleaner; + } + + public CleaningCachingResultSetMapper(final Cleaner cleaner, final Calendar cal, final int arrayMaxLength, final Map> cache) { + super(cal, arrayMaxLength, cache); + this.cleaner = cleaner; + } + + public CleaningCachingResultSetMapper(final Cleaner cleaner, final int arrayMaxLength, final Map> cache) { + super(arrayMaxLength, cache); + this.cleaner = cleaner; + } + + public CleaningCachingResultSetMapper(final Cleaner cleaner, final Map> cache) { + super(cache); + this.cleaner = cleaner; + } + + public CleaningCachingResultSetMapper(final Cleaner cleaner, final Calendar cal, final int arrayMaxLength, final boolean threadSafe) { + super(cal, arrayMaxLength, threadSafe); + this.cleaner = cleaner; + } + + public CleaningCachingResultSetMapper(final Cleaner cleaner, final int arrayMaxLength, final boolean threadSafe) { + super(arrayMaxLength, threadSafe); + this.cleaner = cleaner; + } + + public CleaningCachingResultSetMapper(final Cleaner cleaner, final boolean threadSafe) { + super(threadSafe); + this.cleaner = cleaner; + } + + @Override + @SuppressWarnings({"unchecked"}) + public RowMapper getRowMapper(final ResultSet resultSet, final Class returnTypeClass, final Calendar cal, final Class mapValType, final Class mapKeyType) { + return new CleaningRowToObjectMapper((Cleaner)cleaner, super.getRowMapper(resultSet, returnTypeClass, cal, mapValType, mapKeyType)); + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningCompilingResultSetMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningCompilingResultSetMapper.java new file mode 100644 index 0000000..1416368 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningCompilingResultSetMapper.java @@ -0,0 +1,68 @@ +package com.moparisthebest.jdbc; + +import java.sql.ResultSet; +import java.util.Calendar; +import java.util.Map; + +/** + * Created by mopar on 5/18/17. + */ +public class CleaningCompilingResultSetMapper extends CompilingResultSetMapper { + + private final Cleaner cleaner; + + public CleaningCompilingResultSetMapper(final Cleaner cleaner, final Calendar cal, final int arrayMaxLength, final int maxEntries) { + super(cal, arrayMaxLength, maxEntries); + this.cleaner = cleaner; + } + + public CleaningCompilingResultSetMapper(final Cleaner cleaner, final Calendar cal, final int arrayMaxLength) { + super(cal, arrayMaxLength); + this.cleaner = cleaner; + } + + public CleaningCompilingResultSetMapper(final Cleaner cleaner, final int arrayMaxLength) { + super(arrayMaxLength); + this.cleaner = cleaner; + } + + public CleaningCompilingResultSetMapper(final Cleaner cleaner) { + this.cleaner = cleaner; + } + + public CleaningCompilingResultSetMapper(final Cleaner cleaner, final Calendar cal, final int arrayMaxLength, final Map> cache) { + super(cal, arrayMaxLength, cache); + this.cleaner = cleaner; + } + + public CleaningCompilingResultSetMapper(final Cleaner cleaner, final int arrayMaxLength, final Map> cache) { + super(arrayMaxLength, cache); + this.cleaner = cleaner; + } + + public CleaningCompilingResultSetMapper(final Cleaner cleaner, final Map> cache) { + super(cache); + this.cleaner = cleaner; + } + + public CleaningCompilingResultSetMapper(final Cleaner cleaner, final Calendar cal, final int arrayMaxLength, final boolean threadSafe) { + super(cal, arrayMaxLength, threadSafe); + this.cleaner = cleaner; + } + + public CleaningCompilingResultSetMapper(final Cleaner cleaner, final int arrayMaxLength, final boolean threadSafe) { + super(arrayMaxLength, threadSafe); + this.cleaner = cleaner; + } + + public CleaningCompilingResultSetMapper(final Cleaner cleaner, final boolean threadSafe) { + super(threadSafe); + this.cleaner = cleaner; + } + + @Override + @SuppressWarnings({"unchecked"}) + public RowMapper getRowMapper(final ResultSet resultSet, final Class returnTypeClass, final Calendar cal, final Class mapValType, final Class mapKeyType) { + return new CleaningRowToObjectMapper((Cleaner)cleaner, super.getRowMapper(resultSet, returnTypeClass, cal, mapValType, mapKeyType)); + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningResultSetMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningResultSetMapper.java index 5139003..36f7729 100644 --- a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningResultSetMapper.java +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningResultSetMapper.java @@ -19,7 +19,7 @@ public class CleaningResultSetMapper extends ResultSetMapper { @Override @SuppressWarnings({"unchecked"}) - protected RowToObjectMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { + public RowMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { return new CleaningRowToObjectMapper((Cleaner)cleaner, resultSet, returnTypeClass, cal, mapValType, mapKeyType); } } diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningRowToObjectMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningRowToObjectMapper.java index bfe7ea9..3252b37 100644 --- a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningRowToObjectMapper.java +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/CleaningRowToObjectMapper.java @@ -4,19 +4,27 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.Calendar; -public class CleaningRowToObjectMapper extends RowToObjectMapper { +public class CleaningRowToObjectMapper implements RowMapper { private final Cleaner cleaner; + private final RowMapper delegate; public CleaningRowToObjectMapper(Cleaner cleaner, ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { - super(resultSet, returnTypeClass, cal, mapValType, mapKeyType); - if (cleaner == null) - throw new NullPointerException("cleaner cannot be null!"); + this(cleaner, new RowToObjectMapper(resultSet, returnTypeClass, cal, mapValType, mapKeyType)); + } + + public CleaningRowToObjectMapper(final Cleaner cleaner, final RowMapper delegate) { this.cleaner = cleaner; + this.delegate = delegate; } @Override public T mapRowToReturnType() throws SQLException { - return cleaner.clean(super.mapRowToReturnType()); + return cleaner.clean(delegate.mapRowToReturnType()); + } + + @Override + public K getMapKey() throws SQLException { + return delegate.getMapKey(); } } 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 3ef4c0b..94de5c5 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 @@ -144,7 +144,7 @@ public class CompilingResultSetMapper extends ResultSetMapper { } @Override - protected RowToObjectMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { + public RowMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { return new CompilingRowToObjectMapper(compiler, cache, resultSet, returnTypeClass, cal, mapValType, mapKeyType); } } 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 879a534..8e5b663 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 @@ -21,13 +21,10 @@ import java.util.Map; * 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 { + // do not remove, used from generated classes public static final String firstColumnError = "Cannot call getFirstColumn when mapKeyType is null!"; protected final Compiler compiler; @@ -36,7 +33,11 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { protected String[] keys = null; // for caching if we must generate class public CompilingRowToObjectMapper(final Compiler compiler, final Map> cache, ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { - super(resultSet, returnTypeClass, cal, mapValType, mapKeyType); + this(compiler, cache, resultSet, returnTypeClass, cal, mapValType, mapKeyType, false); + } + + public CompilingRowToObjectMapper(final Compiler compiler, final Map> cache, ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType, final boolean caseInsensitiveMap) { + super(resultSet, returnTypeClass, cal, mapValType, mapKeyType, caseInsensitiveMap); this.compiler = compiler; try { final CachingRowToObjectMapper.ResultSetKey keys = new CachingRowToObjectMapper.ResultSetKey(super.getKeysFromResultSet(), _returnTypeClass, _mapKeyType); @@ -172,7 +173,7 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { } java.append(footer); - System.out.println(java); + //System.out.println(java); return compiler.compile(className, java); } diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java index 7bae004..afe3099 100644 --- a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java @@ -53,7 +53,7 @@ import java.util.concurrent.*; * * @author Travis Burtrum (modifications from beehive) */ -public class ResultSetMapper { +public class ResultSetMapper implements RowMapperProvider { public static final Map interfaceToConcrete = Collections.unmodifiableMap(new HashMap() {{ // Collection's @@ -151,7 +151,7 @@ public class ResultSetMapper { // a value of less than 1 indicates that all rows from the ResultSet should be included. final boolean unlimitedRows = arrayMaxLength < 1; - final RowToObjectMapper rowMapper = getRowMapper(rs, componentType, cal, mapValType, null); + final RowMapper rowMapper = getRowMapper(rs, componentType, cal, mapValType, null); for (; (unlimitedRows || numRows != arrayMaxLength) && rs.next(); ++numRows) { E object = rowMapper.mapRowToReturnType(); @@ -230,7 +230,7 @@ public class ResultSetMapper { // a value of less than 1 indicates that all rows from the ResultSet should be included. final boolean unlimitedRows = arrayMaxLength < 1; - final RowToObjectMapper rowMapper = getRowMapper(rs, componentType, cal, mapValType, mapKeyType); + final RowMapper rowMapper = getRowMapper(rs, componentType, cal, mapValType, mapKeyType); for (; (unlimitedRows || numRows != arrayMaxLength) && rs.next(); ++numRows) { K key = rowMapper.getMapKey(); @@ -311,7 +311,7 @@ public class ResultSetMapper { // a value of less than 1 indicates that all rows from the ResultSet should be included. final boolean unlimitedRows = arrayMaxLength < 1; - final RowToObjectMapper rowMapper = getRowMapper(rs, componentType, cal, mapValType, mapKeyType); + final RowMapper rowMapper = getRowMapper(rs, componentType, cal, mapValType, mapKeyType); for (; (unlimitedRows || numRows != arrayMaxLength) && rs.next(); ++numRows) { K key = rowMapper.getMapKey(); @@ -404,7 +404,7 @@ public class ResultSetMapper { } // fairly un-interesting methods below here - protected RowToObjectMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { + public RowMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { return new RowToObjectMapper(resultSet, returnTypeClass, cal, mapValType, mapKeyType); } diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowMapper.java index de4dd89..beac8c1 100644 --- a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowMapper.java +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowMapper.java @@ -1,264 +1,21 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $Header:$ - */ - package com.moparisthebest.jdbc; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.util.Calendar; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** - * Abstract base class for all row mappers. - * - * RowMappers are used to map the contents of a row in a ResultSet to the return type of an annotated method. - * Supported RowMapper types include: HashMap, Map, Object, XmlObject. When a ResultSetMapper is ready to - * map a ResultSet row to an object, it requests a RowMapper for the return type of the method from the - * RowMapperFactory. - * + * Created by mopar on 5/18/17. */ -public abstract class RowMapper { +public interface RowMapper { + /** + * Map a ResultSet row to the return type class + * @return An instance of class, if _mapKeyType is not null and _columnCount is 2, return only index 2 + */ + T mapRowToReturnType() throws SQLException; - private static final String SETTER_NAME_REGEX = "^(set)([A-Z_]\\w*+)"; - protected static final TypeMappingsFactory _tmf = TypeMappingsFactory.getInstance(); - protected static final Pattern _setterRegex = Pattern.compile(SETTER_NAME_REGEX); - - /** ResultSet to map. */ - protected final ResultSet _resultSet; - - /** Calendar instance for date/time mappings. */ - protected final Calendar _cal; - - /** Class to map ResultSet Rows to. */ - protected final Class _returnTypeClass; - - protected final Class _mapKeyType; - - protected final int _columnCount; - - protected final boolean mapOnlySecondColumn; - - /** - * Create a new RowMapper for the specified ResultSet and return type Class. - * @param resultSet ResultSet to map - * @param returnTypeClass Class to map ResultSet rows to. - * @param cal Calendar instance for date/time values. - */ - protected RowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapKeyType) { - _resultSet = resultSet; - _returnTypeClass = returnTypeClass; - _cal = cal; - _mapKeyType = mapKeyType; - - try { - _columnCount = resultSet.getMetaData().getColumnCount(); - } catch (SQLException e) { - throw new MapperException("RowToObjectMapper: SQLException: " + e.getMessage(), e); - } - - mapOnlySecondColumn = _mapKeyType != null && _columnCount == 2; - } - - protected RowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal) { - this(resultSet, returnTypeClass, cal, null); - } - - /** - * Map a ResultSet row to the return type class - * @return An instance of class, if _mapKeyType is not null and _columnCount is 2, return only index 2 - */ - public abstract T mapRowToReturnType() throws SQLException; - - /** - * key for map - * @return index number 1, with type of _mapKeyType - * @throws MapperException if _mapKeyType is null - */ - public abstract K getMapKey() throws SQLException; - - /** - * Build a String array of column names from the ResultSet. - * @return A String array containing the column names contained within the ResultSet. - * @throws java.sql.SQLException on error - */ - protected String[] getKeysFromResultSet() throws SQLException { - - String[] keys; - final ResultSetMetaData md = _resultSet.getMetaData(); - - keys = new String[_columnCount + 1]; - for (int i = 1; i <= _columnCount; i++) { - keys[i] = md.getColumnName(i).toUpperCase(); - } - return keys; - } - - /** - * Determine if the given method is a java bean setter method. - * @param method Method to check - * @return True if the method is a setter method. - */ - protected boolean isSetterMethod(Method method) { - Matcher matcher = _setterRegex.matcher(method.getName()); - if (matcher.matches()) { - - if (Modifier.isStatic(method.getModifiers())) return false; - if (!Modifier.isPublic(method.getModifiers())) return false; - if (!Void.TYPE.equals(method.getReturnType())) return false; - - // method parameter checks - Class[] params = method.getParameterTypes(); - if (params.length != 1) return false; - if (TypeMappingsFactory.TYPE_UNKNOWN == _tmf.getTypeId(params[0])) return false; - - return true; - } - return false; - } - - /** - * Extract a column value from the ResultSet and return it as resultType. - * - * @param index The column index of the value to extract from the ResultSet. - * @param resultType The return type. Defined in TypeMappingsFactory. - * @return The extracted value - * @throws java.sql.SQLException on error. - */ - protected Object extractColumnValue(int index, int resultType) throws SQLException { - - switch (resultType) { - case TypeMappingsFactory.TYPE_INT: - return new Integer(_resultSet.getInt(index)); - case TypeMappingsFactory.TYPE_LONG: - return new Long(_resultSet.getLong(index)); - case TypeMappingsFactory.TYPE_FLOAT: - return new Float(_resultSet.getFloat(index)); - case TypeMappingsFactory.TYPE_DOUBLE: - return new Double(_resultSet.getDouble(index)); - case TypeMappingsFactory.TYPE_BYTE: - return new Byte(_resultSet.getByte(index)); - case TypeMappingsFactory.TYPE_SHORT: - return new Short(_resultSet.getShort(index)); - case TypeMappingsFactory.TYPE_BOOLEAN: - return _resultSet.getBoolean(index) ? Boolean.TRUE : Boolean.FALSE; - case TypeMappingsFactory.TYPE_INT_OBJ: - { - int i = _resultSet.getInt(index); - return _resultSet.wasNull() ? null : new Integer(i); - } - case TypeMappingsFactory.TYPE_LONG_OBJ: - { - long i = _resultSet.getLong(index); - return _resultSet.wasNull() ? null : new Long(i); - } - case TypeMappingsFactory.TYPE_FLOAT_OBJ: - { - float i = _resultSet.getFloat(index); - return _resultSet.wasNull() ? null : new Float(i); - } - case TypeMappingsFactory.TYPE_DOUBLE_OBJ: - { - double i = _resultSet.getDouble(index); - return _resultSet.wasNull() ? null : new Double(i); - } - case TypeMappingsFactory.TYPE_BYTE_OBJ: - { - byte i = _resultSet.getByte(index); - return _resultSet.wasNull() ? null : new Byte(i); - } - case TypeMappingsFactory.TYPE_SHORT_OBJ: - { - short i = _resultSet.getShort(index); - return _resultSet.wasNull() ? null : new Short(i); - } - case TypeMappingsFactory.TYPE_BOOLEAN_OBJ: - { - boolean i = _resultSet.getBoolean(index); - return _resultSet.wasNull() ? null : (i ? Boolean.TRUE : Boolean.FALSE); - } - case TypeMappingsFactory.TYPE_STRING: - case TypeMappingsFactory.TYPE_XMLBEAN_ENUM: - return _resultSet.getString(index); - case TypeMappingsFactory.TYPE_BIG_DECIMAL: - return _resultSet.getBigDecimal(index); - case TypeMappingsFactory.TYPE_BYTES: - return _resultSet.getBytes(index); - case TypeMappingsFactory.TYPE_TIMESTAMP: - { - if (null == _cal) - return _resultSet.getTimestamp(index); - else - return _resultSet.getTimestamp(index, _cal); - } - case TypeMappingsFactory.TYPE_TIME: - { - if (null == _cal) - return _resultSet.getTime(index); - else - return _resultSet.getTime(index, _cal); - } - case TypeMappingsFactory.TYPE_SQLDATE: - { - if (null == _cal) - return _resultSet.getDate(index); - else - return _resultSet.getDate(index, _cal); - } - case TypeMappingsFactory.TYPE_DATE: - { - // convert explicity to java.util.Date - // 12918 | knex does not return java.sql.Date properly from web service - java.sql.Timestamp ts = (null == _cal) ? _resultSet.getTimestamp(index) : _resultSet.getTimestamp(index, _cal); - if (null == ts) - return null; - return new java.util.Date(ts.getTime()); - } - case TypeMappingsFactory.TYPE_CALENDAR: - { - java.sql.Timestamp ts = (null == _cal) ? _resultSet.getTimestamp(index) : _resultSet.getTimestamp(index, _cal); - if (null == ts) - return null; - Calendar c = (null == _cal) ? Calendar.getInstance() : (Calendar) _cal.clone(); - c.setTimeInMillis(ts.getTime()); - return c; - } - case TypeMappingsFactory.TYPE_REF: - return _resultSet.getRef(index); - case TypeMappingsFactory.TYPE_BLOB: - return _resultSet.getBlob(index); - case TypeMappingsFactory.TYPE_CLOB: - return _resultSet.getClob(index); - case TypeMappingsFactory.TYPE_ARRAY: - return _resultSet.getArray(index); - case TypeMappingsFactory.TYPE_READER: - case TypeMappingsFactory.TYPE_STREAM: - throw new MapperException("streaming return types are not supported by the JdbcControl; use ResultSet instead"); - case TypeMappingsFactory.TYPE_STRUCT: - case TypeMappingsFactory.TYPE_UNKNOWN: - // JAVA_TYPE (could be any), or REF - return _resultSet.getObject(index); - default: - throw new MapperException("internal error: unknown type ID: " + Integer.toString(resultType)); - } - } + /** + * key for map + * @return index number 1, with type of _mapKeyType + * @throws MapperException if _mapKeyType is null + */ + K getMapKey() throws SQLException; } diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowMapperProvider.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowMapperProvider.java new file mode 100644 index 0000000..a7e3a1c --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowMapperProvider.java @@ -0,0 +1,11 @@ +package com.moparisthebest.jdbc; + +import java.sql.ResultSet; +import java.util.Calendar; + +/** + * Created by mopar on 5/18/17. + */ +public interface RowMapperProvider { + RowMapper getRowMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType); +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java index b3bd4a2..251daf0 100644 --- a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowToObjectMapper.java @@ -19,6 +19,8 @@ package com.moparisthebest.jdbc; * $Header:$ */ +import com.moparisthebest.jdbc.util.CaseInsensitiveHashMap; + import java.lang.reflect.*; import java.sql.ResultSet; import java.sql.ResultSetMetaData; @@ -26,6 +28,8 @@ import java.sql.SQLException; import java.util.Calendar; import java.util.HashMap; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static com.moparisthebest.jdbc.UpdateableDTO.YES; import static com.moparisthebest.jdbc.UpdateableDTO.NO; @@ -47,7 +51,11 @@ import static com.moparisthebest.jdbc.UpdateableDTO.NO; * * @author Travis Burtrum (modifications from beehive) */ -public class RowToObjectMapper extends RowMapper { +public class RowToObjectMapper extends AbstractRowMapper { + + private static final String SETTER_NAME_REGEX = "^(set)([A-Z_]\\w*+)"; + protected static final TypeMappingsFactory _tmf = TypeMappingsFactory.getInstance(); + protected static final Pattern _setterRegex = Pattern.compile(SETTER_NAME_REGEX); public static final int TYPE_BOOLEAN = _tmf.getTypeId(Boolean.TYPE);//TypeMappingsFactory.TYPE_BOOLEAN; // not public? public static final int TYPE_BOOLEAN_OBJ = _tmf.getTypeId(Boolean.class);//TypeMappingsFactory.TYPE_BOOLEAN_OBJ; // not public? @@ -82,6 +90,11 @@ public class RowToObjectMapper extends RowMapper { this(resultSet, returnTypeClass, cal, mapValType, null); } + + public RowToObjectMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { + this(resultSet, returnTypeClass, cal, mapValType, mapKeyType, false); + } + /** * Create a new RowToObjectMapper. * @@ -89,11 +102,17 @@ public class RowToObjectMapper extends RowMapper { * @param returnTypeClass Class to map to. * @param cal Calendar instance for date/time mappings. */ - public RowToObjectMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { + public RowToObjectMapper(ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType, boolean caseInsensitiveMap) { super(resultSet, returnTypeClass, cal, mapKeyType); returnMap = Map.class.isAssignableFrom(returnTypeClass); if(returnMap){ - _returnTypeClass = ResultSetMapper.getConcreteClass(returnTypeClass, HashMap.class); + Class rtc = ResultSetMapper.getConcreteClass(returnTypeClass, HashMap.class); + if(caseInsensitiveMap && HashMap.class.equals(rtc)) { + @SuppressWarnings("unchecked") + final Class rtct = (Class) CaseInsensitiveHashMap.class; + rtc = rtct; + } + _returnTypeClass = rtc; componentType = mapValType; }else{ _returnTypeClass = returnTypeClass; @@ -137,7 +156,7 @@ public class RowToObjectMapper extends RowMapper { * like perhaps to implement the original beehive behavior of case-insensitive strings */ @SuppressWarnings({"unchecked"}) - protected Map getMapImplementation() throws IllegalAccessException, InstantiationException { + private Map getMapImplementation() throws IllegalAccessException, InstantiationException { return (Map)_returnTypeClass.newInstance(); } @@ -441,40 +460,178 @@ public class RowToObjectMapper extends RowMapper { } } - @Override + public static T fixNull(Class returnType) { + return returnType.cast(_tmf.fixNull(returnType)); + } + + /** + * Determine if the given method is a java bean setter method. + * @param method Method to check + * @return True if the method is a setter method. + */ + protected boolean isSetterMethod(Method method) { + Matcher matcher = _setterRegex.matcher(method.getName()); + if (matcher.matches()) { + + if (Modifier.isStatic(method.getModifiers())) return false; + if (!Modifier.isPublic(method.getModifiers())) return false; + if (!Void.TYPE.equals(method.getReturnType())) return false; + + // method parameter checks + Class[] params = method.getParameterTypes(); + if (params.length != 1) return false; + if (TypeMappingsFactory.TYPE_UNKNOWN == _tmf.getTypeId(params[0])) return false; + + return true; + } + return false; + } + + /** + * Extract a column value from the ResultSet and return it as resultType. + * + * @param index The column index of the value to extract from the ResultSet. + * @param resultType The return type. Defined in TypeMappingsFactory. + * @return The extracted value + * @throws java.sql.SQLException on error. + */ protected Object extractColumnValue(int index, int resultType) throws SQLException { try{ - if (resultType != TYPE_BOOLEAN && resultType != TYPE_BOOLEAN_OBJ) - return super.extractColumnValue(index, resultType); - else { - // do some special handling to convert a database string to a boolean - boolean ret; - try { - // try to get an actual boolean from the database - ret = _resultSet.getBoolean(index); - // null seems to get returned as false above, so String code won't run if its null - if (_resultSet.wasNull()) - if (resultType == TYPE_BOOLEAN_OBJ) - return null; // only return null for Boolean object - else - throw new MapperException(String.format("Implicit conversion of database string to boolean failed on column '%d'. Returned string needs to be 'Y' or 'N' and was instead 'null'. If you want to accept null values, make it an object Boolean instead of primitive boolean.", index)); - } catch (SQLException e) { - // if we are here, it wasn't a boolean or null, so try to grab a string instead - String bool = _resultSet.getString(index);//.toUpperCase(); // do we want it case-insensitive? - ret = YES.equals(bool); - if (!ret && !NO.equals(bool)) - throw new MapperException(String.format("Implicit conversion of database string to boolean failed on column '%d'. Returned string needs to be 'Y' or 'N' and was instead '%s'.", index, bool)); - //throw e; + switch (resultType) { + case TypeMappingsFactory.TYPE_INT: + return new Integer(_resultSet.getInt(index)); + case TypeMappingsFactory.TYPE_LONG: + return new Long(_resultSet.getLong(index)); + case TypeMappingsFactory.TYPE_FLOAT: + return new Float(_resultSet.getFloat(index)); + case TypeMappingsFactory.TYPE_DOUBLE: + return new Double(_resultSet.getDouble(index)); + case TypeMappingsFactory.TYPE_BYTE: + return new Byte(_resultSet.getByte(index)); + case TypeMappingsFactory.TYPE_SHORT: + return new Short(_resultSet.getShort(index)); + case TypeMappingsFactory.TYPE_INT_OBJ: + { + int i = _resultSet.getInt(index); + return _resultSet.wasNull() ? null : new Integer(i); } - return ret ? Boolean.TRUE : Boolean.FALSE; + case TypeMappingsFactory.TYPE_LONG_OBJ: + { + long i = _resultSet.getLong(index); + return _resultSet.wasNull() ? null : new Long(i); + } + case TypeMappingsFactory.TYPE_FLOAT_OBJ: + { + float i = _resultSet.getFloat(index); + return _resultSet.wasNull() ? null : new Float(i); + } + case TypeMappingsFactory.TYPE_DOUBLE_OBJ: + { + double i = _resultSet.getDouble(index); + return _resultSet.wasNull() ? null : new Double(i); + } + case TypeMappingsFactory.TYPE_BYTE_OBJ: + { + byte i = _resultSet.getByte(index); + return _resultSet.wasNull() ? null : new Byte(i); + } + case TypeMappingsFactory.TYPE_SHORT_OBJ: + { + short i = _resultSet.getShort(index); + return _resultSet.wasNull() ? null : new Short(i); + } + + case TypeMappingsFactory.TYPE_BOOLEAN: + case TypeMappingsFactory.TYPE_BOOLEAN_OBJ: + { + // do some special handling to convert a database string to a boolean + boolean ret; + try { + // try to get an actual boolean from the database + ret = _resultSet.getBoolean(index); + // null seems to get returned as false above, so String code won't run if its null + if (_resultSet.wasNull()) + if (resultType == TYPE_BOOLEAN_OBJ) + return null; // only return null for Boolean object + else + throw new MapperException(String.format("Implicit conversion of database string to boolean failed on column '%d'. Returned string needs to be 'Y' or 'N' and was instead 'null'. If you want to accept null values, make it an object Boolean instead of primitive boolean.", index)); + } catch (SQLException e) { + // if we are here, it wasn't a boolean or null, so try to grab a string instead + String bool = _resultSet.getString(index);//.toUpperCase(); // do we want it case-insensitive? + ret = YES.equals(bool); + if (!ret && !NO.equals(bool)) + throw new MapperException(String.format("Implicit conversion of database string to boolean failed on column '%d'. Returned string needs to be 'Y' or 'N' and was instead '%s'.", index, bool)); + //throw e; + } + return ret ? Boolean.TRUE : Boolean.FALSE; + } + case TypeMappingsFactory.TYPE_STRING: + case TypeMappingsFactory.TYPE_XMLBEAN_ENUM: + return _resultSet.getString(index); + case TypeMappingsFactory.TYPE_BIG_DECIMAL: + return _resultSet.getBigDecimal(index); + case TypeMappingsFactory.TYPE_BYTES: + return _resultSet.getBytes(index); + case TypeMappingsFactory.TYPE_TIMESTAMP: + { + if (null == _cal) + return _resultSet.getTimestamp(index); + else + return _resultSet.getTimestamp(index, _cal); + } + case TypeMappingsFactory.TYPE_TIME: + { + if (null == _cal) + return _resultSet.getTime(index); + else + return _resultSet.getTime(index, _cal); + } + case TypeMappingsFactory.TYPE_SQLDATE: + { + if (null == _cal) + return _resultSet.getDate(index); + else + return _resultSet.getDate(index, _cal); + } + case TypeMappingsFactory.TYPE_DATE: + { + // convert explicity to java.util.Date + // 12918 | knex does not return java.sql.Date properly from web service + java.sql.Timestamp ts = (null == _cal) ? _resultSet.getTimestamp(index) : _resultSet.getTimestamp(index, _cal); + if (null == ts) + return null; + return new java.util.Date(ts.getTime()); + } + case TypeMappingsFactory.TYPE_CALENDAR: + { + java.sql.Timestamp ts = (null == _cal) ? _resultSet.getTimestamp(index) : _resultSet.getTimestamp(index, _cal); + if (null == ts) + return null; + Calendar c = (null == _cal) ? Calendar.getInstance() : (Calendar) _cal.clone(); + c.setTimeInMillis(ts.getTime()); + return c; + } + case TypeMappingsFactory.TYPE_REF: + return _resultSet.getRef(index); + case TypeMappingsFactory.TYPE_BLOB: + return _resultSet.getBlob(index); + case TypeMappingsFactory.TYPE_CLOB: + return _resultSet.getClob(index); + case TypeMappingsFactory.TYPE_ARRAY: + return _resultSet.getArray(index); + case TypeMappingsFactory.TYPE_READER: + case TypeMappingsFactory.TYPE_STREAM: + throw new MapperException("streaming return types are not supported by the JdbcControl; use ResultSet instead"); + case TypeMappingsFactory.TYPE_STRUCT: + case TypeMappingsFactory.TYPE_UNKNOWN: + // JAVA_TYPE (could be any), or REF + return _resultSet.getObject(index); + default: + throw new MapperException("internal error: unknown type ID: " + Integer.toString(resultType)); } }catch(SQLException e){ throw new SQLExceptionColumnNum(e, index); } } - - public static T fixNull(Class returnType) { - return returnType.cast(_tmf.fixNull(returnType)); - } } diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/util/CaseInsensitiveHashMap.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/util/CaseInsensitiveHashMap.java new file mode 100644 index 0000000..0a39542 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/util/CaseInsensitiveHashMap.java @@ -0,0 +1,18 @@ +package com.moparisthebest.jdbc.util; + +import java.util.HashMap; + +/** + * Created by mopar on 5/18/17. + */ +public class CaseInsensitiveHashMap extends HashMap { + @Override + public V get(Object key) { + return super.get(key instanceof java.lang.String ? ((java.lang.String) key).toLowerCase() : key); + } + + @Override + public boolean containsKey(Object key) { + return super.containsKey(key instanceof java.lang.String ? ((java.lang.String) key).toLowerCase() : key); + } +} diff --git a/beehive-jdbc-mapper/src/test/java/com/moparisthebest/jdbc/CleaningQueryMapperTest.java b/beehive-jdbc-mapper/src/test/java/com/moparisthebest/jdbc/CleaningQueryMapperTest.java new file mode 100644 index 0000000..ca3cec3 --- /dev/null +++ b/beehive-jdbc-mapper/src/test/java/com/moparisthebest/jdbc/CleaningQueryMapperTest.java @@ -0,0 +1,81 @@ +package com.moparisthebest.jdbc; + +import com.moparisthebest.jdbc.dto.*; +import org.junit.*; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.util.*; + +import static com.moparisthebest.jdbc.QueryMapperTest.fieldPerson1; +import static com.moparisthebest.jdbc.QueryMapperTest.getConnection; +import static com.moparisthebest.jdbc.QueryMapperTest.personRegular; +import static com.moparisthebest.jdbc.TryClose.tryClose; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +/** + * Created by mopar on 6/10/14. + */ +@RunWith(Parameterized.class) +public class CleaningQueryMapperTest { + + private static Connection conn; + + @BeforeClass + public static void setUp() throws Throwable { + conn = getConnection(); + } + + @AfterClass + public static void tearDown() throws Throwable { + tryClose(conn); + } + + protected QueryMapper qm; + protected final ResultSetMapper rsm; + + public CleaningQueryMapperTest(final ResultSetMapper rsm) { + this.rsm = rsm; + } + + @Before + public void open() { + this.qm = new QueryMapper(conn, rsm); + } + + @After + public void close() { + tryClose(qm); + } + + @Parameterized.Parameters(name="{0}") + public static Collection getParameters() + { + final Cleaner personCleaner = new Cleaner() { + @Override + public FieldPerson clean(final FieldPerson dto) { + dto.firstName += " " + dto.lastName; + dto.lastName = null; + return dto; + } + }; + return Arrays.asList(new Object[][] { + { new CleaningResultSetMapper(personCleaner) }, + { new CleaningCachingResultSetMapper(personCleaner) }, + { new CleaningCompilingResultSetMapper(personCleaner) }, + }); + } + + // fields + + @Test + public void testFieldRegularPerson() throws Throwable { + final Person expected = fieldPerson1; + final Person actual = qm.toObject(personRegular, expected.getClass(), expected.getPersonNo()); + assertEquals(expected.getFirstName() + " " + expected.getLastName(), actual.getFirstName()); + assertNull(actual.getLastName()); + } +} diff --git a/beehive-jdbc-mapper/src/test/java/com/moparisthebest/jdbc/QueryMapperTest.java b/beehive-jdbc-mapper/src/test/java/com/moparisthebest/jdbc/QueryMapperTest.java index c46f527..1ef2324 100644 --- a/beehive-jdbc-mapper/src/test/java/com/moparisthebest/jdbc/QueryMapperTest.java +++ b/beehive-jdbc-mapper/src/test/java/com/moparisthebest/jdbc/QueryMapperTest.java @@ -10,6 +10,8 @@ import java.sql.DriverManager; import java.util.*; import static com.moparisthebest.jdbc.TryClose.tryClose; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; /** * Created by mopar on 6/10/14. @@ -19,48 +21,47 @@ public class QueryMapperTest { private static Connection conn; - protected static final Person fieldPerson1 = new FieldPerson(1, new Date(0), "First", "Person"); - protected static final Boss fieldBoss1 = new FieldBoss(2, new Date(0), "Second", "Person", "Finance", "Second"); - protected static final Boss fieldBoss2 = new FieldBoss(3, new Date(0), "Third", "Person", "Finance", null); - protected static final Boss fieldBoss3 = new FieldBoss(4, new Date(0), null, "Person", "Finance", "Fourth"); + public static final Person fieldPerson1 = new FieldPerson(1, new Date(0), "First", "Person"); + public static final Boss fieldBoss1 = new FieldBoss(2, new Date(0), "Second", "Person", "Finance", "Second"); + public static final Boss fieldBoss2 = new FieldBoss(3, new Date(0), "Third", "Person", "Finance", null); + public static final Boss fieldBoss3 = new FieldBoss(4, new Date(0), null, "Person", "Finance", "Fourth"); - protected static final Person setPerson1 = new SetPerson(fieldPerson1); - protected static final Boss setBoss1 = new SetBoss(fieldBoss1); - protected static final Boss setBoss2 = new SetBoss(fieldBoss2); - protected static final Boss setBoss3 = new SetBoss(fieldBoss3); + public static final Person setPerson1 = new SetPerson(fieldPerson1); + public static final Boss setBoss1 = new SetBoss(fieldBoss1); + public static final Boss setBoss2 = new SetBoss(fieldBoss2); + public static final Boss setBoss3 = new SetBoss(fieldBoss3); - protected static final Person reverseFieldPerson1 = new ReverseFieldPerson(fieldPerson1); - protected static final Boss reverseFieldBoss1 = new ReverseFieldBoss(fieldBoss1); - protected static final Boss reverseFieldBoss2 = new ReverseFieldBoss(fieldBoss2); - protected static final Boss reverseFieldBoss3 = new ReverseFieldBoss(fieldBoss3); + public static final Person reverseFieldPerson1 = new ReverseFieldPerson(fieldPerson1); + public static final Boss reverseFieldBoss1 = new ReverseFieldBoss(fieldBoss1); + public static final Boss reverseFieldBoss2 = new ReverseFieldBoss(fieldBoss2); + public static final Boss reverseFieldBoss3 = new ReverseFieldBoss(fieldBoss3); - protected static final Person reverseSetPerson1 = new ReverseSetPerson(fieldPerson1); - protected static final Boss reverseSetBoss1 = new ReverseSetBoss(fieldBoss1); - protected static final Boss reverseSetBoss2 = new ReverseSetBoss(fieldBoss2); - protected static final Boss reverseSetBoss3 = new ReverseSetBoss(fieldBoss3); + public static final Person reverseSetPerson1 = new ReverseSetPerson(fieldPerson1); + public static final Boss reverseSetBoss1 = new ReverseSetBoss(fieldBoss1); + public static final Boss reverseSetBoss2 = new ReverseSetBoss(fieldBoss2); + public static final Boss reverseSetBoss3 = new ReverseSetBoss(fieldBoss3); - protected static final String personRegular = "SELECT * FROM person WHERE person_no = ?"; - protected static final String bossRegularAndUnderscore = "SELECT p.person_no, p.first_name AS firstName, p.last_name, p.birth_date, b.department, p.first_name " + + public static final String personRegular = "SELECT * FROM person WHERE person_no = ?"; + public static final String bossRegularAndUnderscore = "SELECT p.person_no, p.first_name AS firstName, p.last_name, p.birth_date, b.department, p.first_name " + "FROM person p " + "JOIN boss b ON p.person_no = b.person_no " + "WHERE p.person_no = ?"; - protected static final String bossRegularAndUnderscoreReverse = "SELECT p.person_no, p.first_name, p.last_name, p.birth_date, b.department, p.first_name AS firstName " + + public static final String bossRegularAndUnderscoreReverse = "SELECT p.person_no, p.first_name, p.last_name, p.birth_date, b.department, p.first_name AS firstName " + "FROM person p " + "JOIN boss b ON p.person_no = b.person_no " + "WHERE p.person_no = ?"; - protected static final String bossRegular = "SELECT p.person_no, p.first_name AS firstName, p.last_name, p.birth_date, b.department " + + public static final String bossRegular = "SELECT p.person_no, p.first_name AS firstName, p.last_name, p.birth_date, b.department " + "FROM person p " + "JOIN boss b ON p.person_no = b.person_no " + "WHERE p.person_no = ?"; - protected static final String bossUnderscore = "SELECT p.person_no, p.first_name, p.last_name, p.birth_date, b.department " + + public static final String bossUnderscore = "SELECT p.person_no, p.first_name, p.last_name, p.birth_date, b.department " + "FROM person p " + "JOIN boss b ON p.person_no = b.person_no " + "WHERE p.person_no = ?"; - @BeforeClass - public static void setUp() throws Throwable { + public static Connection getConnection() throws Throwable { Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance(); - conn = DriverManager.getConnection("jdbc:derby:memory:derbyDB;create=true"); + final Connection conn = DriverManager.getConnection("jdbc:derby:memory:derbyDB;create=true"); QueryMapper qm = null; try { qm = new QueryMapper(conn); @@ -75,6 +76,12 @@ public class QueryMapperTest { } finally { tryClose(qm); } + return conn; + } + + @BeforeClass + public static void setUp() throws Throwable { + conn = getConnection(); } @AfterClass @@ -105,6 +112,7 @@ public class QueryMapperTest { return Arrays.asList(new Object[][] { { new ResultSetMapper() }, { new CachingResultSetMapper() }, + { new CaseInsensitiveMapResultSetMapper() }, { new CompilingResultSetMapper() }, }); } @@ -231,7 +239,7 @@ public class QueryMapperTest { @Test public void testSelectArrayMap() throws Throwable { final List> arrayMap = getListMap(); - Assert.assertEquals(arrayMap.toArray(new Map[arrayMap.size()]), qm.toArrayMap("SELECT first_name, last_name FROM person WHERE person_no < 4", arrayMap.get(0).getClass(), String.class)); + Assert.assertArrayEquals(arrayMap.toArray(new Map[arrayMap.size()]), qm.toArrayMap("SELECT first_name, last_name FROM person WHERE person_no < 4", arrayMap.get(0).getClass(), String.class)); } @Test @@ -325,4 +333,32 @@ public class QueryMapperTest { */ Assert.assertEquals(expected, actual); } + + @Test + public void testCaseInsensitiveMap() throws Throwable { + final Map map = qm.toListMap("SELECT 'bob' as bob, 'tom' as tom FROM person WHERE person_no = ?", String.class, 1).get(0); + if (rsm instanceof CaseInsensitiveMapResultSetMapper) { + assertEquals("bob", map.get("bob")); + assertEquals("bob", map.get("Bob")); + assertEquals("bob", map.get("BoB")); + assertEquals("bob", map.get("BOb")); + assertEquals("bob", map.get("BOB")); + assertEquals("tom", map.get("tom")); + assertEquals("tom", map.get("Tom")); + assertEquals("tom", map.get("ToM")); + assertEquals("tom", map.get("TOm")); + assertEquals("tom", map.get("TOM")); + } else { + assertEquals("bob", map.get("bob")); + assertNull(map.get("Bob")); + assertNull(map.get("BoB")); + assertNull(map.get("BOb")); + assertNull(map.get("BOB")); + assertEquals("tom", map.get("tom")); + assertNull(map.get("Tom")); + assertNull(map.get("ToM")); + assertNull(map.get("TOm")); + assertNull(map.get("TOM")); + } + } }