265 lines
11 KiB
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));
|
|
}
|
|
}
|
|
}
|