JdbcMapper/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/RowMapper.java

265 lines
11 KiB
Java

/*
* 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.
*
*/
public abstract class RowMapper<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);
/** 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));
}
}
}