阅读 dbutils 源码(三)
BeanProcessor 类
这个类主要是辅助RowProcessor
这个接口的实现类,例如BasicRowProcessor
中就默认使用BeanProcessor
,当然也可以自己重写一个BeanProcessor
,然后作为参数在BasicRowProcessor
构造函数中传进去即可。
先看一下BeanProcessor
中的方法:
其中toBean
和toBeanList
这两个方法比较重要,上一篇博客也提到过,在BasicRowProcessor
就是调用的这两个方法,将结果集转成对应的bean或bean集合。
toBean 的实现
toBean
方法:
public <T> T toBean(final ResultSet rs, final Class<? extends T> type) throws SQLException {
//反射创建一个对象
final T bean = this.newInstance(type);
//将结果集映射到对象
return this.populateBean(rs, bean);
}
populateBean
方法:
public <T> T populateBean(final ResultSet rs, final T bean) throws SQLException {
//PropertyDescriptor是对javabean属性的描述,可以据此获取属性的名称、类型、set/get方法等,前提是必须是符合标准的javabean,必须有set/get方法,boolean类型的可以用is代替get。
final PropertyDescriptor[] props = this.propertyDescriptors(bean.getClass());
final ResultSetMetaData rsmd = rs.getMetaData();
//列与属性序号对应
final int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
//给bean赋值
return populateBean(rs, bean, props, columnToProperty);
}
private <T> T populateBean(final ResultSet rs, final T bean,
final PropertyDescriptor[] props, final int[] columnToProperty)
throws SQLException {
for (int i = 1; i < columnToProperty.length; i++) {
//列没有对应的属性
if (columnToProperty[i] == PROPERTY_NOT_FOUND) {
continue;
}
//找到列对应的属性描述
final PropertyDescriptor prop = props[columnToProperty[i]];
//属性类型
final Class<?> propType = prop.getPropertyType();
Object value = null;
if (propType != null) {
//取出ResultSet中第i列的值,并转化成propType类型。
value = this.processColumn(rs, i, propType);
if (value == null && propType.isPrimitive()) {
//是原始类型并且为空,提供默认值。
value = primitiveDefaults.get(propType);
}
}
//调用javabean的set方法为属性赋值
this.callSetter(bean, prop, value);
}
return bean;
}
这里列出两个主要的方法,其他的方法可以下载我github中的源码,我都写有中文注解。
toBeanList 的实现
toBeanList
方法:
public <T> List<T> toBeanList(final ResultSet rs, final Class<? extends T> type) throws SQLException {
final List<T> results = new ArrayList<>();
if (!rs.next()) {
return results;
}
final PropertyDescriptor[] props = this.propertyDescriptors(type);
final ResultSetMetaData rsmd = rs.getMetaData();
final int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
do {
results.add(this.createBean(rs, type, props, columnToProperty));
} while (rs.next());
return results;
}
createBean
方法:
private <T> T createBean(final ResultSet rs, final Class<T> type,
final PropertyDescriptor[] props, final int[] columnToProperty)
throws SQLException {
final T bean = this.newInstance(type);
return populateBean(rs, bean, props, columnToProperty);
}
这个是很好理解的,看懂了前面toBean
的实现,这里就自然而然理解了。
梳理一下:从 ResultSet 到 Bean
- 反射创建
Bean
对象 - 获取
Bean
属性的描述数组PropertyDescriptor[]
- 获取
ResultSet
结果集中字段的信息ResultSetMetaData
- 匹配
Bean
属性和ResultSet
结果集中字段,如果没有自定义列名与属性对应的map
,那么默认使用忽略大小写的属性名与列名对应,此时下划线与驼峰是无法匹配的,不过可以用BeanProcessor
的子类GenerousBeanProcessor
解决 - 给
Bean
的属性赋值,赋值成功的前提是属性要与对应的字段匹配上,并且java类型要与数据库字段类型对应上