本文重点
以ResultSetHandler的实现类BeanListHandler为例,探索dbutils的QueryRunner的实现细节,重点是如何给java bean类对象赋值。
public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
PreparedStatement stmt = null;
ResultSet rs = null;
T result = null;
try {
//开始预处理
stmt = this.prepareStatement(conn, sql);
//注入参数
this.fillStatement(stmt, params);
//执行sql
rs = this.wrap(stmt.executeQuery());
//处理结果
//重点进入这里
result = rsh.handle(rs);
} catch (SQLException var33) {
this.rethrow(var33, sql, params);
} finally {
try {
this.close(rs);
} finally {
this.close((Statement)stmt);
}
}
return result;
}
进入BeanListHandler下的方法
public List<T> handle(ResultSet rs) throws SQLException {
//这里的this.convert是实现RowProcessor接口的BasicRowProcessor的实例
//type是bean的class类对象
return this.convert.toBeanList(rs, this.type);
}
进入BasicRowProcessor下的方法
public <T> List<T> toBeanList(ResultSet rs, Class<T> type) throws SQLException {
//convert是BeanProcessor类的实例
return this.convert.toBeanList(rs, type);
}
step into BeanProcessor
这个是重点要观察的方法
public <T> List<T> toBeanList(ResultSet rs, Class<T> type) throws SQLException {
List<T> results = new ArrayList();
if (!rs.next()) {
return results;
} else {
//props数组记录了bean类的每一个成员变量的名字和读写方法的名字,即get和set方法。
PropertyDescriptor[] props = this.propertyDescriptors(type);
//rsmd记录了元数据
ResultSetMetaData rsmd = rs.getMetaData();
int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
do {
results.add(this.createBean(rs, type, props, columnToProperty));
} while(rs.next());
return results;
}
}
props数组记录了bean类的每一个成员变量的名字和读写方法的名字,即get和set方法。
rsmd记录了元数据
step into mapColumnsToProperties()
protected int[] mapColumnsToProperties(ResultSetMetaData rsmd, PropertyDescriptor[] props) throws SQLException {
// cols表示元数据的列
int cols = rsmd.getColumnCount();
int[] columnToProperty = new int[cols + 1];
Arrays.fill(columnToProperty, -1);
// 元数据的列从1开始
for(int col = 1; col <= cols; ++col) {
// 获取元数据的列的别名
String columnName = rsmd.getColumnLabel(col);
// 如果没有别名则获取字段名
if (null == columnName || 0 == columnName.length()) {
columnName = rsmd.getColumnName(col);
}
for(int i = 0; i < props.length; ++i) {
// sql中的字段名或别名忽略大小写后和bean的field名称做对比,若相同则写入对应关系的数组。
if (columnName.equalsIgnoreCase(props[i].getName())) {
columnToProperty[col] = i;
break;
}
}
}
return columnToProperty;
}
step into createBean
private <T> T createBean(ResultSet rs, Class<T> type, PropertyDescriptor[] props, int[] columnToProperty) throws SQLException {
//创建bean类实例
T bean = this.newInstance(type);
for(int i = 1; i < columnToProperty.length; ++i) {
if (columnToProperty[i] != -1) {
PropertyDescriptor prop = props[columnToProperty[i]];
//得到变量类型
Class<?> propType = prop.getPropertyType();
//根据索引和变量类型取出值来
Object value = this.processColumn(rs, i, propType);
//如果值为null,则给默认值
if (propType != null && value == null && propType.isPrimitive()) {
value = primitiveDefaults.get(propType);
}
this.callSetter(bean, prop, value);
}
}
return bean;
}
最后进入callSetter方法
private void callSetter(Object target, PropertyDescriptor prop, Object value) throws SQLException {
//获取字段的set方法
Method setter = prop.getWriteMethod();
if (setter != null) {
Class<?>[] params = setter.getParameterTypes();
try {
if (value != null && value instanceof Date) {
if (params[0].getName().equals("java.sql.Date")) {
value = new java.sql.Date(((Date)value).getTime());
} else if (params[0].getName().equals("java.sql.Time")) {
value = new Time(((Date)value).getTime());
} else if (params[0].getName().equals("java.sql.Timestamp")) {
value = new Timestamp(((Date)value).getTime());
}
}
//判断字段类型和value值是否兼容,若是则调用setter.invoke进行赋值。
if (this.isCompatibleType(value, params[0])) {
setter.invoke(target, value);
} else {
throw new SQLException("Cannot set " + prop.getName() + ": incompatible types.");
}
} catch (IllegalArgumentException var7) {
throw new SQLException("Cannot set " + prop.getName() + ": " + var7.getMessage());
} catch (IllegalAccessException var8) {
throw new SQLException("Cannot set " + prop.getName() + ": " + var8.getMessage());
} catch (InvocationTargetException var9) {
throw new SQLException("Cannot set " + prop.getName() + ": " + var9.getMessage());
}
}
}
这样我们就把一行表记录添加到List中了。