ResultSetHandler
之前说过在创建StatementHandler处理器时会同时创建ParameterHandler及ResultSetHandler。
ResultSetHandler是Mybatis的核心组件,主要负责将结果集resultSets转化成结果列表(或cursor)和处理储存过程的输出。
源码分析
ResultSet
在原生JDBC查询的代码中,使用Statement进行操作,会返回ResultSet对象
。
ResultSet也是java.sql中的接口,它表示通过执行查询数据库的语句生成的结果集对象,在JDBC的操作中数据库的所有查询记录将使用ResultSet进行接收。
我们获取到ResultSet后,就可以将其转换为程序中的JAVA对象进行数据展示,但是原生的操作非常繁琐,所以Mybatis提供了ResultSet处理器,我们只需要定义好返回类型,Mybatis就可以自动进行转换映射了。
ResultSetHandler
ResultSetHandler是一个接口,也只有一个实现类,ResultSetHandler定义了一些处理结果集的方法。
/**
* @author DDKK.COM 弟弟快看,程序员编程资料站
* 结果集处理器
*/
public interface ResultSetHandler {
//处理结果集
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
// 处理游标
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
//处理结果集
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
DefaultResultSetHandler
DefaultResultSetHandler是默认的结果集处理器,其中实现了很多处理一对一、一对多、嵌套查询等结果集的处理方法。实在太多了,后面再分析某一个。
流程分析
1. 创建ResultSetHandler
创建StatementHandler时,构造方法进入BaseStatementHandler,调用configuration的方法开始创建ResultSetHandler处理器。
之后会调用DefaultResultSetHandler的构造方法,并且也使用了拦截器包装。
DefaultResultSetHandler构造方法中,也设置了执行器、configuration、boundSql等重要参数,其中有一个reflectorFactory反射工厂,因为转为我们需要的对象是通过反射机制来实现的。
2. 结果集处理
执行了查询之后,数据库的结果集就设置到了Statement对象中,接下来就是需要把Statement中的数据转换处理了。
首先调用DefaultResultSetHandler的handleResultSets方法处理Statement。
3. 结果集包装
handleResultSets首先调用getFirstResultSet获取ResultSetWrapper。
// ResultSet包装
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
// 获取ResultSet
ResultSet rs = stmt.getResultSet();
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB 2.1)
// 是否有多个ResultSet
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
}
}
}
// 包装
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
调用ResultSetWrapper构造方法创建包装类,会封装当前查询返回的列名、数据类型等。
public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
super();
// 类型处理器
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.resultSet = rs;
// 元数据 ,表名、列名、数据库中数据类型等
// com.mysql.cj.result.Field@668a32a4[dbName=angel_admin,tableName=base_user,originalTableName=base_user,columnName=user_id,originalColumnName=user_id,mysqlType=8(FIELD_TYPE_BIGINT),sqlType=-5,flags= AUTO_INCREMENT PRIMARY_KEY, charsetIndex=63, charsetName=ISO-8859-1]
final ResultSetMetaData metaData = rs.getMetaData();
// 列数 14
final int columnCount = metaData.getColumnCount();
// 循环所有列,从configuration配置中,获取对应信息
for (int i = 1; i <= columnCount; i++) {
// 列名集合
columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
// JDBC类型集合
jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
// 返回的数据类型集合
classNames.add(metaData.getColumnClassName(i));
}
}
4. 获取ResultMap
下一步通过mappedStatement获取我们配置的ResultMap。
5. 反射创建对象
可以看出返回对象使用反射机制创建的。
6. 返回数据
经过比较复杂的结果集处理,返回了我们想要的数据。