mirror of
https://github.com/moparisthebest/JdbcMapper
synced 2024-12-21 23:08:52 -05:00
Major refactor, more tests
This commit is contained in:
parent
e7639d740c
commit
efd1d44808
@ -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<K, T> implements RowMapper<K,T> {
|
||||
|
||||
/** 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<T> _returnTypeClass;
|
||||
|
||||
protected final Class<K> _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<T> returnTypeClass, Calendar cal, Class<K> 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<T> 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;
|
||||
}
|
||||
}
|
@ -132,7 +132,7 @@ public class CachingResultSetMapper extends ResultSetMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <K, T> RowToObjectMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
public <K, T> RowMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
return new CachingRowToObjectMapper<K, T>(cache, resultSet, returnTypeClass, cal, mapValType, mapKeyType);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ public class CaseInsensitiveMapResultSetMapper extends ResultSetMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <K, T> RowToObjectMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
return new CaseInsensitiveMapRowToObjectMapper<K, T>(resultSet, returnTypeClass, cal, mapValType, mapKeyType);
|
||||
public <K, T> RowMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
return new RowToObjectMapper<K, T>(resultSet, returnTypeClass, cal, mapValType, mapKeyType, true);
|
||||
}
|
||||
}
|
||||
|
@ -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<K, T> extends RowToObjectMapper<K, T> {
|
||||
public CaseInsensitiveMapRowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass) {
|
||||
super(resultSet, returnTypeClass);
|
||||
}
|
||||
|
||||
public CaseInsensitiveMapRowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass, Class<?> mapValType) {
|
||||
super(resultSet, returnTypeClass, mapValType);
|
||||
}
|
||||
|
||||
public CaseInsensitiveMapRowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal) {
|
||||
super(resultSet, returnTypeClass, cal);
|
||||
}
|
||||
|
||||
public CaseInsensitiveMapRowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType) {
|
||||
super(resultSet, returnTypeClass, cal, mapValType);
|
||||
}
|
||||
|
||||
public CaseInsensitiveMapRowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
super(resultSet, returnTypeClass, cal, mapValType, mapKeyType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> getMapImplementation() throws IllegalAccessException, InstantiationException {
|
||||
if(HashMap.class.equals(_returnTypeClass))
|
||||
return new HashMap<String, Object>(){
|
||||
@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();
|
||||
}
|
||||
}
|
@ -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<E> extends CachingResultSetMapper {
|
||||
|
||||
private final Cleaner<E> cleaner;
|
||||
|
||||
public CleaningCachingResultSetMapper(final Cleaner<E> cleaner, final Calendar cal, final int arrayMaxLength, final int maxEntries) {
|
||||
super(cal, arrayMaxLength, maxEntries);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCachingResultSetMapper(final Cleaner<E> cleaner, final Calendar cal, final int arrayMaxLength) {
|
||||
super(cal, arrayMaxLength);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCachingResultSetMapper(final Cleaner<E> cleaner, final int arrayMaxLength) {
|
||||
super(arrayMaxLength);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCachingResultSetMapper(final Cleaner<E> cleaner) {
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCachingResultSetMapper(final Cleaner<E> cleaner, final Calendar cal, final int arrayMaxLength, final Map<CachingRowToObjectMapper.ResultSetKey, CachingRowToObjectMapper.FieldMapping<?>> cache) {
|
||||
super(cal, arrayMaxLength, cache);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCachingResultSetMapper(final Cleaner<E> cleaner, final int arrayMaxLength, final Map<CachingRowToObjectMapper.ResultSetKey, CachingRowToObjectMapper.FieldMapping<?>> cache) {
|
||||
super(arrayMaxLength, cache);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCachingResultSetMapper(final Cleaner<E> cleaner, final Map<CachingRowToObjectMapper.ResultSetKey, CachingRowToObjectMapper.FieldMapping<?>> cache) {
|
||||
super(cache);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCachingResultSetMapper(final Cleaner<E> cleaner, final Calendar cal, final int arrayMaxLength, final boolean threadSafe) {
|
||||
super(cal, arrayMaxLength, threadSafe);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCachingResultSetMapper(final Cleaner<E> cleaner, final int arrayMaxLength, final boolean threadSafe) {
|
||||
super(arrayMaxLength, threadSafe);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCachingResultSetMapper(final Cleaner<E> cleaner, final boolean threadSafe) {
|
||||
super(threadSafe);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public <K, T> RowMapper<K, T> getRowMapper(final ResultSet resultSet, final Class<T> returnTypeClass, final Calendar cal, final Class<?> mapValType, final Class<K> mapKeyType) {
|
||||
return new CleaningRowToObjectMapper((Cleaner<T>)cleaner, super.getRowMapper(resultSet, returnTypeClass, cal, mapValType, mapKeyType));
|
||||
}
|
||||
}
|
@ -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<E> extends CompilingResultSetMapper {
|
||||
|
||||
private final Cleaner<E> cleaner;
|
||||
|
||||
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final Calendar cal, final int arrayMaxLength, final int maxEntries) {
|
||||
super(cal, arrayMaxLength, maxEntries);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final Calendar cal, final int arrayMaxLength) {
|
||||
super(cal, arrayMaxLength);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final int arrayMaxLength) {
|
||||
super(arrayMaxLength);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner) {
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final Calendar cal, final int arrayMaxLength, final Map<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?, ?>> cache) {
|
||||
super(cal, arrayMaxLength, cache);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final int arrayMaxLength, final Map<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?, ?>> cache) {
|
||||
super(arrayMaxLength, cache);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final Map<CachingRowToObjectMapper.ResultSetKey, CompilingRowToObjectMapper.ResultSetToObject<?, ?>> cache) {
|
||||
super(cache);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final Calendar cal, final int arrayMaxLength, final boolean threadSafe) {
|
||||
super(cal, arrayMaxLength, threadSafe);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final int arrayMaxLength, final boolean threadSafe) {
|
||||
super(arrayMaxLength, threadSafe);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
public CleaningCompilingResultSetMapper(final Cleaner<E> cleaner, final boolean threadSafe) {
|
||||
super(threadSafe);
|
||||
this.cleaner = cleaner;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public <K, T> RowMapper<K, T> getRowMapper(final ResultSet resultSet, final Class<T> returnTypeClass, final Calendar cal, final Class<?> mapValType, final Class<K> mapKeyType) {
|
||||
return new CleaningRowToObjectMapper((Cleaner<T>)cleaner, super.getRowMapper(resultSet, returnTypeClass, cal, mapValType, mapKeyType));
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ public class CleaningResultSetMapper<E> extends ResultSetMapper {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked"})
|
||||
protected <K, T> RowToObjectMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
public <K, T> RowMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
return new CleaningRowToObjectMapper<K, T>((Cleaner<T>)cleaner, resultSet, returnTypeClass, cal, mapValType, mapKeyType);
|
||||
}
|
||||
}
|
||||
|
@ -4,19 +4,27 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Calendar;
|
||||
|
||||
public class CleaningRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
|
||||
public class CleaningRowToObjectMapper<K, T> implements RowMapper<K, T> {
|
||||
|
||||
private final Cleaner<T> cleaner;
|
||||
private final RowMapper<K,T> delegate;
|
||||
|
||||
public CleaningRowToObjectMapper(Cleaner<T> cleaner, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
super(resultSet, returnTypeClass, cal, mapValType, mapKeyType);
|
||||
if (cleaner == null)
|
||||
throw new NullPointerException("cleaner cannot be null!");
|
||||
this(cleaner, new RowToObjectMapper<K, T>(resultSet, returnTypeClass, cal, mapValType, mapKeyType));
|
||||
}
|
||||
|
||||
public CleaningRowToObjectMapper(final Cleaner<T> cleaner, final RowMapper<K,T> 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();
|
||||
}
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ public class CompilingResultSetMapper extends ResultSetMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <K, T> RowToObjectMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
public <K, T> RowMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
return new CompilingRowToObjectMapper<K, T>(compiler, cache, resultSet, returnTypeClass, cal, mapValType, mapKeyType);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* 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<K, T> extends RowToObjectMapper<K, T> {
|
||||
|
||||
// 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<K, T> extends RowToObjectMapper<K, T> {
|
||||
protected String[] keys = null; // for caching if we must generate class
|
||||
|
||||
public CompilingRowToObjectMapper(final Compiler compiler, final Map<CachingRowToObjectMapper.ResultSetKey, ResultSetToObject<?,?>> cache, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
super(resultSet, returnTypeClass, cal, mapValType, mapKeyType);
|
||||
this(compiler, cache, resultSet, returnTypeClass, cal, mapValType, mapKeyType, false);
|
||||
}
|
||||
|
||||
public CompilingRowToObjectMapper(final Compiler compiler, final Map<CachingRowToObjectMapper.ResultSetKey, ResultSetToObject<?,?>> cache, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> 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<K, T> extends RowToObjectMapper<K, T> {
|
||||
}
|
||||
|
||||
java.append(footer);
|
||||
System.out.println(java);
|
||||
//System.out.println(java);
|
||||
return compiler.compile(className, 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<Class, Class> interfaceToConcrete = Collections.unmodifiableMap(new HashMap<Class, Class>() {{
|
||||
// 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<?, E> rowMapper = getRowMapper(rs, componentType, cal, mapValType, null);
|
||||
final RowMapper<?, E> 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<K, E> rowMapper = getRowMapper(rs, componentType, cal, mapValType, mapKeyType);
|
||||
final RowMapper<K, E> 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<K, C> rowMapper = getRowMapper(rs, componentType, cal, mapValType, mapKeyType);
|
||||
final RowMapper<K, C> 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 <K, T> RowToObjectMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
public <K, T> RowMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
return new RowToObjectMapper<K, T>(resultSet, returnTypeClass, cal, mapValType, mapKeyType);
|
||||
}
|
||||
|
||||
|
@ -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<K, T> {
|
||||
public interface RowMapper<K,T> {
|
||||
/**
|
||||
* 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<T> _returnTypeClass;
|
||||
|
||||
protected final Class<K> _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<T> returnTypeClass, Calendar cal, Class<K> 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<T> 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;
|
||||
}
|
||||
|
@ -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 {
|
||||
<K, T> RowMapper<K, T> getRowMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType);
|
||||
}
|
@ -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<K, T> extends RowMapper<K, T> {
|
||||
public class RowToObjectMapper<K, T> extends AbstractRowMapper<K, T> {
|
||||
|
||||
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<K, T> extends RowMapper<K, T> {
|
||||
this(resultSet, returnTypeClass, cal, mapValType, null);
|
||||
}
|
||||
|
||||
|
||||
public RowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
this(resultSet, returnTypeClass, cal, mapValType, mapKeyType, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RowToObjectMapper.
|
||||
*
|
||||
@ -89,11 +102,17 @@ public class RowToObjectMapper<K, T> extends RowMapper<K, T> {
|
||||
* @param returnTypeClass Class to map to.
|
||||
* @param cal Calendar instance for date/time mappings.
|
||||
*/
|
||||
public RowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||
public RowToObjectMapper(ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType, boolean caseInsensitiveMap) {
|
||||
super(resultSet, returnTypeClass, cal, mapKeyType);
|
||||
returnMap = Map.class.isAssignableFrom(returnTypeClass);
|
||||
if(returnMap){
|
||||
_returnTypeClass = ResultSetMapper.getConcreteClass(returnTypeClass, HashMap.class);
|
||||
Class<? extends T> rtc = ResultSetMapper.getConcreteClass(returnTypeClass, HashMap.class);
|
||||
if(caseInsensitiveMap && HashMap.class.equals(rtc)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<? extends T> rtct = (Class<? extends T>) CaseInsensitiveHashMap.class;
|
||||
rtc = rtct;
|
||||
}
|
||||
_returnTypeClass = rtc;
|
||||
componentType = mapValType;
|
||||
}else{
|
||||
_returnTypeClass = returnTypeClass;
|
||||
@ -137,7 +156,7 @@ public class RowToObjectMapper<K, T> extends RowMapper<K, T> {
|
||||
* like perhaps to implement the original beehive behavior of case-insensitive strings
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
protected Map<String, Object> getMapImplementation() throws IllegalAccessException, InstantiationException {
|
||||
private Map<String, Object> getMapImplementation() throws IllegalAccessException, InstantiationException {
|
||||
return (Map<String, Object>)_returnTypeClass.newInstance();
|
||||
}
|
||||
|
||||
@ -441,40 +460,178 @@ public class RowToObjectMapper<K, T> extends RowMapper<K, T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public static <T> T fixNull(Class<T> 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> T fixNull(Class<T> returnType) {
|
||||
return returnType.cast(_tmf.fixNull(returnType));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,18 @@
|
||||
package com.moparisthebest.jdbc.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Created by mopar on 5/18/17.
|
||||
*/
|
||||
public class CaseInsensitiveHashMap<String, V> extends HashMap<String, V> {
|
||||
@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);
|
||||
}
|
||||
}
|
@ -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<Object[]> getParameters()
|
||||
{
|
||||
final Cleaner<FieldPerson> personCleaner = new Cleaner<FieldPerson>() {
|
||||
@Override
|
||||
public FieldPerson clean(final FieldPerson dto) {
|
||||
dto.firstName += " " + dto.lastName;
|
||||
dto.lastName = null;
|
||||
return dto;
|
||||
}
|
||||
};
|
||||
return Arrays.asList(new Object[][] {
|
||||
{ new CleaningResultSetMapper<FieldPerson>(personCleaner) },
|
||||
{ new CleaningCachingResultSetMapper<FieldPerson>(personCleaner) },
|
||||
{ new CleaningCompilingResultSetMapper<FieldPerson>(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());
|
||||
}
|
||||
}
|
@ -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<Map<String, String>> 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<String, String> 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user