在dbutils中,BeanProcessor是一个很重要的类:将列和bean对象的属性进行匹配,将列的值赋予bean的对象。这个是使用了反射来进行的。
来看下类的outline:
从上图可以看出,有“创建对象”、“调用setter”、“获得类的属性描述符”等方法,还有一个最重要的方法mapColumnsToProperties(ResultSetMetaData,PropertyDescriptor),这个方法是将结果集和类的属性进行一个匹配。toBean是将一行记录转换成一个bean对象。toBeanList就是将多行记录变成bean对象的List集合。
下面看一下它的具体实现。
/*
* 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.
*/
package org.apache.commons.dbutils;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* <code>BeanProcessor</code> matches column names to bean property names
* and converts <code>ResultSet</code> columns into objects for those bean
* properties. Subclasses should override the methods in the processing chain
* to customize behavior.
* </p>
* BeanProcessor将列名和bean的属性名进行匹配;将ResultSet的列转换成对象的属性。想要定制行为,可以覆盖这些方法。
*
* <p>
* This class is thread-safe.
* </p>
*
* @see BasicRowProcessor
*
* @since DbUtils 1.1
*/
public class BeanProcessor {
/**
* Special array value used by <code>mapColumnsToProperties</code> that
* indicates there is no bean property that matches a column from a
* <code>ResultSet</code>.
* <p>
* 特殊的数组值,给mapColumnsToProperties使用。表明了在ResultSet中某一列
* 在javabean中没有对应的属性
* </p>
*/
protected static final int PROPERTY_NOT_FOUND = -1;
/**
* Set a bean's primitive properties to these defaults when SQL NULL
* is returned. These are the same as the defaults that ResultSet get*
* methods return in the event of a NULL column.
* <p>
* 以基本数据类型为key,以0和FALSE为默认值
* </p>
*/
private static final Map<Class<?>, Object> primitiveDefaults = new HashMap<Class<?>, Object>();
/**
* ResultSet column to bean property name overrides.
*/
private final Map<String, String> columnToPropertyOverrides;
static {
primitiveDefaults.put(Integer.TYPE, Integer.valueOf(0));
primitiveDefaults.put(Short.TYPE, Short.valueOf((short) 0));
primitiveDefaults.put(Byte.TYPE, Byte.valueOf((byte) 0));
primitiveDefaults.put(Float.TYPE, Float.valueOf(0f));
primitiveDefaults.put(Double.TYPE, Double.valueOf(0d));
primitiveDefaults.put(Long.TYPE, Long.valueOf(0L));
primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
primitiveDefaults.put(Character.TYPE, Character.valueOf((char) 0));
}
/**
* Constructor for BeanProcessor.
*/
public BeanProcessor() {
this(new HashMap<String, String>());
}
/**
* Constructor for BeanProcessor configured with column to property name overrides.
*
* @param columnToPropertyOverrides ResultSet column to bean property name overrides
* @since 1.5
*/
public BeanProcessor(Map<String, String> columnToPropertyOverrides) {
super();
if (columnToPropertyOverrides == null) {
throw new IllegalArgumentException("columnToPropertyOverrides map cannot be null");
}
this.columnToPropertyOverrides = columnToPropertyOverrides;
}
/**
* Convert a <code>ResultSet</code> row into a JavaBean. This
* implementation uses reflection and <code>BeanInfo</code> classes to
* match column names to bean property names. Properties are matched to
* columns based on several factors:
* <br/>
* <ol>
* <li>
* The class has a writable property with the same name as a column.
* The name comparison is case insensitive.
* </li>
*
* <li>
* The column type can be converted to the property's set method
* parameter type with a ResultSet.get* method. If the conversion fails
* (ie. the property was an int and the column was a Timestamp) an
* SQLException is thrown.
* </li>
* </ol>
*
* <p>
* Primitive bean properties are set to their defaults when SQL NULL is
* returned from the <code>ResultSet</code>. Numeric fields are set to 0
* and booleans are set to false. Object bean properties are set to
* <code>null</code> when SQL NULL is returned. This is the same behavior
* as the <code>ResultSet</code> get* methods.
* </p>
* @param <T> The type of bean to create
* @param rs ResultSet that supplies the bean data
* @param type Class from which to create the bean instance
* @throws SQLException if a database access error occurs
* @return the newly created bean
*/
public <T> T toBean(ResultSet rs, Class<T> type) throws SQLException {
//获得类的属性描述符
PropertyDescriptor[] props = this.propertyDescriptors(type);
//获得ResultSet的元数据
ResultSetMetaData rsmd = rs.getMetaData();
int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
return this.createBean(rs, type, props, columnToProperty);
}
/**
* Convert a <code>ResultSet</code> into a <code>List</code> of JavaBeans.
* This implementation uses reflection and <code>BeanInfo</code> classes to
* match column names to bean property names. Properties are matched to
* columns based on several factors:
* <br/>
* <ol>
* <li>
* The class has a writable property with the same name as a column.
* The name comparison is case insensitive.
* </li>
*
* <li>
* The column type can be converted to the property's set method
* parameter type with a ResultSet.get* method. If the conversion fails
* (ie. the property was an int and the column was a Timestamp) an
* SQLException is thrown.
* </li>
* </ol>
*
* <p>
* Primitive bean properties are set to their defaults when SQL NULL is
* returned from the <code>ResultSet</code>. Numeric fields are set to 0
* and booleans are set to false. Object bean properties are set to
* <code>null</code> when SQL NULL is returned. This is the same behavior
* as the <code>ResultSet</code> get* methods.
* </p>
* @param <T> The type of bean to create
* @param rs ResultSet that supplies the bean data
* @param type Class from which to create the bean instance
* @throws SQLException if a database access error occurs
* @return the newly created List of beans
*/
public <T> List<T> toBeanList(ResultSet rs, Class<T> type) throws SQLException {
List<T> results = new ArrayList<T>();
//如果ResultSet中没有记录,那么就返回一个size为0的集合List
if (!rs.next()) {
return results;
}
PropertyDescriptor[] props = this.propertyDescriptors(type);
ResultSetMetaData rsmd = rs.getMetaData();
int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
// TODO 这里为什么要使用do while,使用while也可以实现啊
do {
//调用一次createBean,就产生一个bean对象
results.add(this.createBean(rs, type, props, columnToProperty));
} while (rs.next());
//返回一个有内容的集合List
return results;
}
/**
* Creates a new object and initializes its fields from the ResultSet.
* 创建一个object,实例化object的字段,值来自ResultSet的一行记录
* @param <T> The type of bean to create
* @param rs The result set.
* @param type The bean type (the return type of the object).
* @param props The property descriptors.
* @param columnToProperty The column indices in the result set.
* @return An initialized object.
* @throws SQLException if a database error occurs.
*/
private <T> T createBean(ResultSet rs, Class<T> type,
PropertyDescriptor[] props, int[] columnToProperty)
throws SQLException {
//创建出一个对象
T bean = this.newInstance(type);
/*
* 从1开始计算,因为0存储的没意义
* i为列数
*/
for (int i = 1; i < columnToProperty.length; i++) {
//如果值为-1,那么就说明此列在属性描述符数组中没有对应的属性
if (columnToProperty[i] == PROPERTY_NOT_FOUND) {
continue;
}
//根据列数拿到对应的属性描述符
PropertyDescriptor prop = props[columnToProperty[i]];
//拿到属性的类型
Class<?> propType = prop.getPropertyType();
//获得列值
Object value = this.processColumn(rs, i, propType);
//属性的类型不为null,值为null,且为基本类型,就给予一个默认的值
if (propType != null && value == null && propType.isPrimitive()) {
value = primitiveDefaults.get(propType);
}
//然后调用此属性的setter方法,设置值
this.callSetter(bean, prop, value);
}
//当循环结束后,就将bean中能赋值的属性都赋值了
return bean;
}
/**
* Calls the setter method on the target object for the given property.
* If no setter method exists for the property, this method does nothing.
* <p>
* 调用目标对象指定属性的setter方法。如果没有setter方法,那么此方法就终止了
* </p>
* @param target The object to set the property on.
* @param prop The property to set.
* @param value The value to pass into the setter.
* @throws SQLException if an error occurs setting the property.
*/
private void callSetter(Object target, PropertyDescriptor prop, Object value)
throws SQLException {
//获得指定属性的setter方法
Method setter = prop.getWriteMethod();
//setter为null,那么就终止了,什么也不干
if (setter == null) {
return;
}
//获得setter方法的参数类型
Class<?>[] params = setter.getParameterTypes();
try {
// convert types for some popular ones
if (value instanceof java.util.Date) {
//拿出setter方法的参数类型名字
final String targetType = params[0].getName();
if ("java.sql.Date".equals(targetType)) {
value = new java.sql.Date(((java.util.Date) value).getTime());
} else
if ("java.sql.Time".equals(targetType)) {
value = new java.sql.Time(((java.util.Date) value).getTime());
} else
if ("java.sql.Timestamp".equals(targetType)) {
value = new java.sql.Timestamp(((java.util.Date) value).getTime());
}
}
// Don't call setter if the value object isn't the right type
if (this.isCompatibleType(value, params[0])) {
setter.invoke(target, new Object[]{value});
} else {
throw new SQLException(
"Cannot set " + prop.getName() + ": incompatible types, cannot convert "
+ value.getClass().getName() + " to " + params[0].getName());
// value cannot be null here because isCompatibleType allows null
}
} catch (IllegalArgumentException e) {
throw new SQLException(
"Cannot set " + prop.getName() + ": " + e.getMessage());
} catch (IllegalAccessException e) {
throw new SQLException(
"Cannot set " + prop.getName() + ": " + e.getMessage());
} catch (InvocationTargetException e) {
throw new SQLException(
"Cannot set " + prop.getName() + ": " + e.getMessage());
}
}
/**
* ResultSet.getObject() returns an Integer object for an INT column. The
* setter method for the property might take an Integer or a primitive int.
* This method returns true if the value can be successfully passed into
* the setter method. Remember, Method.invoke() handles the unwrapping
* of Integer into an int.
*
* @param value The value to be passed into the setter method.
* @param type The setter's parameter type (non-null)
* @return boolean True if the value is compatible (null => true)
*/
private boolean isCompatibleType(Object value, Class<?> type) {
// Do object check first, then primitives
if (value == null || type.isInstance(value)) {
return true;
} else if (type.equals(Integer.TYPE) && Integer.class.isInstance(value)) {
return true;
} else if (type.equals(Long.TYPE) && Long.class.isInstance(value)) {
return true;
} else if (type.equals(Double.TYPE) && Double.class.isInstance(value)) {
return true;
} else if (type.equals(Float.TYPE) && Float.class.isInstance(value)) {
return true;
} else if (type.equals(Short.TYPE) && Short.class.isInstance(value)) {
return true;
} else if (type.equals(Byte.TYPE) && Byte.class.isInstance(value)) {
return true;
} else if (type.equals(Character.TYPE) && Character.class.isInstance(value)) {
return true;
} else if (type.equals(Boolean.TYPE) && Boolean.class.isInstance(value)) {
return true;
}
return false;
}
/**
* Factory method that returns a new instance of the given Class. This
* is called at the start of the bean creation process and may be
* overridden to provide custom behavior like returning a cached bean
* instance.
* 工厂方法,根据给定的Class返回一个新的实例(使用了反射)。在bean创建流程的开始
* 会调用此方法。
* @param <T> The type of object to create
* @param c The Class to create an object from.
* @return A newly created object of the Class.
* @throws SQLException if creation failed.
*/
protected <T> T newInstance(Class<T> c) throws SQLException {
try {
//使用反射创建对象。使用的是默认无参构造函数。
return c.newInstance();
} catch (InstantiationException e) {
throw new SQLException(
"Cannot create " + c.getName() + ": " + e.getMessage());
} catch (IllegalAccessException e) {
throw new SQLException(
"Cannot create " + c.getName() + ": " + e.getMessage());
}
}
/**
* Returns a PropertyDescriptor[] for the given Class.
*
* @param c The Class to retrieve PropertyDescriptors for.
* @return A PropertyDescriptor[] describing the Class.
* @throws SQLException if introspection failed.
*/
private PropertyDescriptor[] propertyDescriptors(Class<?> c)
throws SQLException {
// Introspector caches BeanInfo classes for better performance
BeanInfo beanInfo = null;
try {
beanInfo = Introspector.getBeanInfo(c);
} catch (IntrospectionException e) {
throw new SQLException(
"Bean introspection failed: " + e.getMessage());
}
return beanInfo.getPropertyDescriptors();
}
/**
* The positions in the returned array represent column numbers. The
* values stored at each position represent the index in the
* <code>PropertyDescriptor[]</code> for the bean property that matches
* the column name. If no bean property was found for a column, the
* position is set to <code>PROPERTY_NOT_FOUND</code>.
* <p>
* 返回的int数组中的位置代表着列的号数。每个位置上的值代表着一个bean的
* PropertyDescriptor[]中的第几个元素
* </p>
* 可以说columnToProperty中的index代表着列的号数(除了0)
* 每个元素的值代表的PropertyDescriptor[]的第几个元素
* 如:columnToProperty[1] = 0;那么就是列1对应着PropertyDescriptor[]
* 的第0个元素。这个int[]数组将列数和属性描述符之间的对应关系保存了起来
* @param rsmd The <code>ResultSetMetaData</code> containing column
* information.
*
* @param props The bean property descriptors.
*
* @throws SQLException if a database access error occurs
*
* @return An int[] with column index to property index mappings. The 0th
* element is meaningless because JDBC column indexing starts at 1.
*/
protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,
PropertyDescriptor[] props) throws SQLException {
//获得ResultSet的列数
int cols = rsmd.getColumnCount();
/*
* 数组的lenght比列数大1,数组的第0个元素不放置东西
* 数组的第1个元素与列1对应,一次类推
*/
int[] columnToProperty = new int[cols + 1];
//将数组中的所有元素填充为-1
Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);
for (int col = 1; col <= cols; col++) {
//先获得列的ColumnLabel,如果没有,就改获得列名
String columnName = rsmd.getColumnLabel(col);
if (null == columnName || 0 == columnName.length()) {
columnName = rsmd.getColumnName(col);
}
//columnToPropertyOverrides默认在无参构造函数内有赋值
String propertyName = columnToPropertyOverrides.get(columnName);
if (propertyName == null) {
propertyName = columnName;
}
for (int i = 0; i < props.length; i++) {
//propertyName如果等于属性描述符的name,使用0以上的数字覆盖掉原来的-1
if (propertyName.equalsIgnoreCase(props[i].getName())) {
columnToProperty[col] = i;
break;
}
}
}
return columnToProperty;
}
/**
* Convert a <code>ResultSet</code> column into an object. Simple
* implementations could just call <code>rs.getObject(index)</code> while
* more complex implementations could perform type manipulation to match
* the column's type to the bean property type.
*
* <p>
* This implementation calls the appropriate <code>ResultSet</code> getter
* method for the given property type to perform the type conversion. If
* the property type doesn't match one of the supported
* <code>ResultSet</code> types, <code>getObject</code> is called.
* </p>
*
* @param rs The <code>ResultSet</code> currently being processed. It is
* positioned on a valid row before being passed into this method.
*
* @param index The current column index being processed.
*
* @param propType The bean property type that this column needs to be
* converted into.
*
* @throws SQLException if a database access error occurs
*
* @return The object from the <code>ResultSet</code> at the given column
* index after optional type processing or <code>null</code> if the column
* value was SQL NULL.
*/
protected Object processColumn(ResultSet rs, int index, Class<?> propType)
throws SQLException {
//如果属性不是基本类型的,而且对应的列的值为null,那么返回null
if ( !propType.isPrimitive() && rs.getObject(index) == null ) {
return null;
}
/*
* 如果属性是String类型的,就取出值
* 如果是基本类型或基本类型的包装类,那么就取出值
* 其他的Object类型,直接getObject
*/
if (propType.equals(String.class)) {
return rs.getString(index);
} else if (
propType.equals(Integer.TYPE) || propType.equals(Integer.class)) {
return Integer.valueOf(rs.getInt(index));
} else if (
propType.equals(Boolean.TYPE) || propType.equals(Boolean.class)) {
return Boolean.valueOf(rs.getBoolean(index));
} else if (propType.equals(Long.TYPE) || propType.equals(Long.class)) {
return Long.valueOf(rs.getLong(index));
} else if (
propType.equals(Double.TYPE) || propType.equals(Double.class)) {
return Double.valueOf(rs.getDouble(index));
} else if (
propType.equals(Float.TYPE) || propType.equals(Float.class)) {
return Float.valueOf(rs.getFloat(index));
} else if (
propType.equals(Short.TYPE) || propType.equals(Short.class)) {
return Short.valueOf(rs.getShort(index));
} else if (propType.equals(Byte.TYPE) || propType.equals(Byte.class)) {
return Byte.valueOf(rs.getByte(index));
} else if (propType.equals(Timestamp.class)) {
return rs.getTimestamp(index);
} else if (propType.equals(SQLXML.class)) {
return rs.getSQLXML(index);
} else {
return rs.getObject(index);
}
}
}
总的来说:BeanProcessor就是对bean的操作。
---------------------------mini分割线-----------------------------
下面来看一个接口:RowProcessor。它定义了如下方法:
toArray:将记录变成数组
toBean:将记录变成对象
toBeanList:将记录变成bean的集合
toMap:将记录变成Map对象
它有一个实现类:BasicRowProcessor。
BasicRowProcessor实现了对ResultSet的处理。实际上是BasicRowProcessor和BeanProcessor一起合作的。
下面看下BasicRowProcessor的源码:
/*
* 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.
*/
package org.apache.commons.dbutils;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* Basic implementation of the <code>RowProcessor</code> interface.
* RowProcessor接口的基本实现
* 有些方法实现委托给了BeanProcessor对象实现
* <p>
* This class is thread-safe.
* </p>
*
* @see RowProcessor
*/
public class BasicRowProcessor implements RowProcessor {
/**
* The default BeanProcessor instance to use if not supplied in the
* constructor.
* 一个BeanProcessor对象
*/
private static final BeanProcessor defaultConvert = new BeanProcessor();
/**
* The Singleton instance of this class.
* 单例
*/
private static final BasicRowProcessor instance = new BasicRowProcessor();
/**
* Returns the Singleton instance of this class.
*
* @return The single instance of this class.
* @deprecated Create instances with the constructors instead. This will
* be removed after DbUtils 1.1.
*/
@Deprecated
public static BasicRowProcessor instance() {
return instance;
}
/**
* Use this to process beans.
*/
private final BeanProcessor convert;
/**
* BasicRowProcessor constructor. Bean processing defaults to a
* BeanProcessor instance.
* <p>
* BasicRowProcessor构造器。Bean处理默认使用了一个BeanProcessor对象
* </p>
*/
public BasicRowProcessor() {
this(defaultConvert);
}
/**
* BasicRowProcessor constructor.
* @param convert The BeanProcessor to use when converting columns to
* bean properties.
* @since DbUtils 1.1
*/
public BasicRowProcessor(BeanProcessor convert) {
super();
this.convert = convert;
}
/**
* Convert a <code>ResultSet</code> row into an <code>Object[]</code>.
* This implementation copies column values into the array in the same
* order they're returned from the <code>ResultSet</code>. Array elements
* will be set to <code>null</code> if the column was SQL NULL.
* 将一个ResultSet中的一行转换成一个Object[],每一个列值就是数组中的一个元素。
* 这个实现将列的值拷贝到数组中,两者时间
* 的顺序一致。如果列值为SQL NULL,那么在数组中就会设置为null
* @see org.apache.commons.dbutils.RowProcessor#toArray(java.sql.ResultSet)
* @param rs ResultSet that supplies the array data
* @throws SQLException if a database access error occurs
* @return the newly created array
*/
@Override
public Object[] toArray(ResultSet rs) throws SQLException {
ResultSetMetaData meta = rs.getMetaData();
//获得列数
int cols = meta.getColumnCount();
//根据列数,创建一个对应大小的数组
Object[] result = new Object[cols];
//将列值放到数组中
for (int i = 0; i < cols; i++) {
result[i] = rs.getObject(i + 1);
}
//返回结果
return result;
}
/**
* Convert a <code>ResultSet</code> row into a JavaBean. This
* implementation delegates to a BeanProcessor instance.
* 将ResultSet中的一行转换成一个javaBean。具体实现委派给一个BeanProcessor实例。
* @see org.apache.commons.dbutils.RowProcessor#toBean(java.sql.ResultSet, java.lang.Class)
* @see org.apache.commons.dbutils.BeanProcessor#toBean(java.sql.ResultSet, java.lang.Class)
* @param <T> The type of bean to create
* @param rs ResultSet that supplies the bean data
* @param type Class from which to create the bean instance
* @throws SQLException if a database access error occurs
* @return the newly created bean
*/
@Override
public <T> T toBean(ResultSet rs, Class<T> type) throws SQLException {
return this.convert.toBean(rs, type);
}
/**
* Convert a <code>ResultSet</code> into a <code>List</code> of JavaBeans.
* This implementation delegates to a BeanProcessor instance.
* 将一个ResultSet转换成一个javaBean集合。具体实现委派给一个BeanProcessor实例。
* @see org.apache.commons.dbutils.RowProcessor#toBeanList(java.sql.ResultSet, java.lang.Class)
* @see org.apache.commons.dbutils.BeanProcessor#toBeanList(java.sql.ResultSet, java.lang.Class)
* @param <T> The type of bean to create
* @param rs ResultSet that supplies the bean data
* @param type Class from which to create the bean instance
* @throws SQLException if a database access error occurs
* @return A <code>List</code> of beans with the given type in the order
* they were returned by the <code>ResultSet</code>.
*/
@Override
public <T> List<T> toBeanList(ResultSet rs, Class<T> type) throws SQLException {
return this.convert.toBeanList(rs, type);
}
/**
* Convert a <code>ResultSet</code> row into a <code>Map</code>. This
* implementation returns a <code>Map</code> with case insensitive column
* names as keys. Calls to <code>map.get("COL")</code> and
* <code>map.get("col")</code> return the same value.
* 将一个ResultSet中的额一行转换成一个Map对象。列名为key,值为value。Map的key不区分
* 大小写。map.get("col")和map.get("COL")都返回同一个值
* @see org.apache.commons.dbutils.RowProcessor#toMap(java.sql.ResultSet)
* @param rs ResultSet that supplies the map data
* @throws SQLException if a database access error occurs
* @return the newly created Map
*/
@Override
public Map<String, Object> toMap(ResultSet rs) throws SQLException {
/*
* 创建一个Map对象,可以看到使用了一个内部类CaseInsensitiveHashMap
* 因为key不管大小写,都将返回同一个值。JDK中没有提供这种类型的Map,
* 所以要自定义了
*/
Map<String, Object> result = new CaseInsensitiveHashMap();
//获得ResultSet的元数据
ResultSetMetaData rsmd = rs.getMetaData();
//获得列数
int cols = rsmd.getColumnCount();
for (int i = 1; i <= cols; i++) {
//以列名为key,列值为value存放到Map中
result.put(rsmd.getColumnName(i), rs.getObject(i));
}
return result;
}
/**
* A Map that converts all keys to lowercase Strings for case insensitive
* lookups. This is needed for the toMap() implementation because
* databases don't consistently handle the casing of column names.
*
* <p>The keys are stored as they are given [BUG #DBUTILS-34], so we maintain
* an internal mapping from lowercase keys to the real keys in order to
* achieve the case insensitive lookup.
*
* <p>Note: This implementation does not allow <tt>null</tt>
* for key, whereas {@link HashMap} does, because of the code:
* <pre>
* key.toString().toLowerCase()
* </pre>
*/
private static class CaseInsensitiveHashMap extends HashMap<String, Object> {
/**
* The internal mapping from lowercase keys to the real keys.
*
* <p>
* Any query operation using the key
* ({@link #get(Object)}, {@link #containsKey(Object)})
* is done in three steps:
* <ul>
* <li>convert the parameter key to lower case</li>
* <li>get the actual key that corresponds to the lower case key</li>
* <li>query the map with the actual key</li>
* </ul>
* </p>
*/
private final Map<String, String> lowerCaseMap = new HashMap<String, String>();
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = -2848100435296897392L;
/** {@inheritDoc} */
@Override
public boolean containsKey(Object key) {
Object realKey = lowerCaseMap.get(key.toString().toLowerCase(Locale.ENGLISH));
return super.containsKey(realKey);
// Possible optimisation here:
// Since the lowerCaseMap contains a mapping for all the keys,
// we could just do this:
// return lowerCaseMap.containsKey(key.toString().toLowerCase());
}
/** {@inheritDoc} */
@Override
public Object get(Object key) {
Object realKey = lowerCaseMap.get(key.toString().toLowerCase(Locale.ENGLISH));
return super.get(realKey);
}
/** {@inheritDoc} */
@Override
public Object put(String key, Object value) {
/*
* In order to keep the map and lowerCaseMap synchronized,
* we have to remove the old mapping before putting the
* new one. Indeed, oldKey and key are not necessaliry equals.
* (That's why we call super.remove(oldKey) and not just
* super.put(key, value))
*/
Object oldKey = lowerCaseMap.put(key.toLowerCase(Locale.ENGLISH), key);
Object oldValue = super.remove(oldKey);
super.put(key, value);
return oldValue;
}
/** {@inheritDoc} */
@Override
public void putAll(Map<? extends String, ?> m) {
for (Map.Entry<? extends String, ?> entry : m.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
this.put(key, value);
}
}
/** {@inheritDoc} */
@Override
public Object remove(Object key) {
Object realKey = lowerCaseMap.remove(key.toString().toLowerCase(Locale.ENGLISH));
return super.remove(realKey);
}
}
}
BasicRowProcessor实现了RowProcessor接口,下面简单讲下方法的实现
1、toArray:将一行记录变成一个数组。根据列数来创建一个对应大小的Object[]。然后将列值取出放到Object[]中。
2、toBean:将一行记录变成一个bean对象。其实它内部调用了BeanProcessor中的toBean方法,具体的可以看看上面BeanProcessor的源码。
3、toBeanList:此方法跟toBean方法类似,也是调用了Beanprocessor中的toBeanList方法。
4、toMap:将一行记录变成一个Map对象。列名为key,列值为value。它使用了一个自定义的CaseInsensitiveHashMap。此map实现了key不区分大小写。这对于我们的使用特别方便。
PS:从01-03,把dbutils中是核心类都讲了,之后的各种Handler主要是调用了BasicRowProcessor和BeanProcessor类。之后会大概讲些Handler。
来源:oschina
链接:https://my.oschina.net/u/198574/blog/164189