在BasicRowProcessor的toBean/toBeanList方法中,没有在方法中直接处理,而是交给BeanProcessor去处理,BeanProcessor主要是利用反射实例化对象,获取属性描述器PropertyDescriptor,调用setter方法,然后返回对象。
1.populateBean 比较关键的一个方法,用于处理属性赋值
private <T> T populateBean(ResultSet rs, T bean, PropertyDescriptor[] props,
int[] columnToProperty) throws SQLException {
/*
由于rs获取的字段顺序和PropertyDescriptor不同,需要columnToProperty来表示他们间的对应关系
如果查询没有对应的属性,则对象的columnToProperty上的值为-1,且数组下标从1开始
*/
for (int i = 1; i < columnToProperty.length; i++) {
//PROPERTY_NOT_FOUND=-1,表示没有和当前ResultSet对应的的字段匹配的属性
if (columnToProperty[i] == PROPERTY_NOT_FOUND) {
continue;
}
//获取属性描述器和当前ResultSet.getObject(i)对应
PropertyDescriptor prop = props[columnToProperty[i]];
//获取属性类型
Class<?> propType = prop.getPropertyType();
Object value = null;
if(propType != null) {
//获取相应的值
value = this.processColumn(rs, i, propType);
/*
这里会对8种基本数据类型的数据处理,如果这8种基本数据类型查询结果为NUL,则根据primitiveDefaults取相应的默认值
不是原始类型则不处理,即为NULL(基本数据类型:boolean、char、byte、short、int、long、float、double)
*/
if (value == null && propType.isPrimitive()) {
//取默认值
value = primitiveDefaults.get(propType);
}
}
//调用setter方法赋值
this.callSetter(bean, prop, value);
}
//set完值后返回对象
return bean;
}
2.再看一个上面用到的方法processColumn
protected Object processColumn(ResultSet rs, int index, Class<?> propType) throws SQLException {
//先获取ResultSet对应的值
Object retval = rs.getObject(index);
//如果查询的值为NULL,且不是基本数据类型则返回NULL
if (!propType.isPrimitive() && retval == null ) {
return null;
}
/*
dbutils提供ColumnHandler的8种基本数据类型子类(char对应为String),是针对ResultSet结果中的对象进行封装,
还有两个SQLXMLColumnHandler/TimestampColumnHandler
ServiceLoader<ColumnHandler>第一次接触这个东西,它可以保存相应的classpath下ColumnHandler的实现类。
这里用于迭代判断类型是否符合
*/
for (ColumnHandler handler : columnHandlers) {
if (handler.match(propType)) {
//类型匹配取相应的值,结束循环,内部只是做一个get操作,然后类型转化一下
retval = handler.apply(rs, index);
break;
}
}
return retval;
}
3.callSetter 属性赋值,调用setter方法
//获取setter方法
protected Method getWriteMethod(Object target, PropertyDescriptor prop, Object value) {
Method method = prop.getWriteMethod();
return method;
}
private void callSetter(Object target, PropertyDescriptor prop, Object value) throws SQLException {
//只要一个PropertyDescriptor 参数就可以啊?不理解为什么这样写
Method setter = getWriteMethod(target, prop, value);
//如果不存在setter方法,或者setter方法参数不是一个的话,直接结束,不是没提供setter方法,就是 不是标准的setter方法
if (setter == null || setter.getParameterTypes().length != 1) {
return;
}
try {
Class<?> firstParam = setter.getParameterTypes()[0];
//针对Bean中的属性进行封装PropertyHandler,主要对时间日期的转化(DatePropertyHandler)
for (PropertyHandler handler : propertyHandlers) {
if (handler.match(firstParam, value)) {
value = handler.apply(firstParam, value);
break;
}
}
//类型检查,检查value是否能转成firstParam的类型
if (this.isCompatibleType(value, firstParam)) {
setter.invoke(target, new Object[]{value});
} else {
throw new SQLException(
"Cannot set " + prop.getName() + ": incompatible types, cannot convert "
+ value.getClass().getName() + " to " + firstParam.getName());
}
} 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());
}
}
前面一些方法算是BeanProcessor最底层的处理了,其他方法都基于这些方法上直接或间接调用
4.<T> T populateBean(ResultSet rs, T bean)
protected int[] mapColumnsToProperties(ResultSetMetaData rsmd, PropertyDescriptor[] props) throws SQLException {
int cols = rsmd.getColumnCount();
//下标从1开始
int[] columnToProperty = new int[cols + 1];
//初始化-1
Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);
for (int col = 1; col <= cols; col++) {
//先查别名
String columnName = rsmd.getColumnLabel(col);
if (null == columnName || 0 == columnName.length()) {
//不存在再查数据库中字段名
columnName = rsmd.getColumnName(col);
}
//查询是否在构造中提供自定义名称
String propertyName = columnToPropertyOverrides.get(columnName);
if (propertyName == null) {
propertyName = columnName;
}
for (int i = 0; i < props.length; i++) {
//设置和ResultSet字段匹配的对应的数组下标
if (propertyName.equalsIgnoreCase(props[i].getName())) {
columnToProperty[col] = i;
break;
}
}
}
return columnToProperty;
}
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();
}
public <T> T populateBean(ResultSet rs, T bean) throws SQLException {
//根据bean获取属性描述器
PropertyDescriptor[] props = this.propertyDescriptors(bean.getClass());
//获取元数据
ResultSetMetaData rsmd = rs.getMetaData();
//因为ResultSet和PropertyDescriptor的属性不一定一一对应,所以需要一个数组标识他们间的对应关系
//如果在构造函数中提供自定义的属性名称,优先级(自定义属性名称>SQL别名>数据库中字段名)
int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
//调用私有populateBean即可
return populateBean(rs, bean, props, columnToProperty);
}
5.对外开放的主要两个方法toBean和toBeanList
//只做实例化bean的操作
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());
}
}
//实例化并初始化bean
private <T> T createBean(ResultSet rs, Class<T> type, PropertyDescriptor[] props, int[] columnToProperty)
throws SQLException {
T bean = this.newInstance(type);
return populateBean(rs, bean, props, columnToProperty);
}
public <T> T toBean(ResultSet rs, Class<? extends T> type) throws SQLException {
//实例化空对象
T bean = this.newInstance(type);
//初始化bean,赋值
return this.populateBean(rs, bean);
}
public <T> List<T> toBeanList(ResultSet rs, Class<? extends T> type) throws SQLException {
List<T> results = new ArrayList<T>();
//没有记录直接返回空列表
if (!rs.next()) {
return results;
}
PropertyDescriptor[] props = this.propertyDescriptors(type);
ResultSetMetaData rsmd = rs.getMetaData();
int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
do {
//直接调用添加即可,再跑个循环结束
results.add(this.createBean(rs, type, props, columnToProperty));
} while (rs.next());
return results;
}
6.剩下的就是构造函数了,和一些静态字段
public BeanProcessor() {
this(new HashMap<String, String>());
}
//提供自定义的属性和查询字段的对应关系Map,不提供则按Bean属性名和查询字段匹配对应
public BeanProcessor(Map<String, String> columnToPropertyOverrides) {
super();
if (columnToPropertyOverrides == null) {
throw new IllegalArgumentException("columnToPropertyOverrides map cannot be null");
}
this.columnToPropertyOverrides = columnToPropertyOverrides;
}
//静态代码块中初始化 基本数据类型为空时的默认值
private static final Map<Class<?>, Object> primitiveDefaults = new HashMap<Class<?>, Object>();
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));
}
//用于加载ColumnHandler的实现类,判断数据库中查询结果的类型
private static final ServiceLoader<ColumnHandler> columnHandlers = ServiceLoader.load(ColumnHandler.class);
//用于加载PropertyHandler的实现类,判断Bean属性的类型
private static final ServiceLoader<PropertyHandler> propertyHandlers = ServiceLoader.load(PropertyHandler.class);
//Bean属性和ResultSet字段对应Map
private final Map<String, String> columnToPropertyOverrides;