1. 概述
之前说过在创建StatementHandler处理器时会同时创建ParameterHandler及ResultSetHandler。
ResultSetHandler是Mybatis的核心组件之一,主要负责将结果集转化成结果列表和处理储存过程的输出。
2. 核心类
2.1 ResultSet
在原生JDBC查询的代码中,使用Statement进行操作,会返回ResultSet对象。
ResultSet也是java.sql中的接口,它表示通过执行查询数据库的语句生成的结果集对象,在JDBC的操作中数据库的所有查询记录将使用ResultSet进行接收。
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try
{
Class.forName(driver);
conn = DriverManager.getConnection(url,user,password);
String sql = "SELECT sNo,sName,sex,age FROM "
+ "Student WHERE dept = '计算机'";
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
while(rs.next())
{
String no = rs.getString("sNo");
String name = rs.getString("sName");
String sex = rs.getString("sex");
int age = rs.getInt("age");
System.out.println(no + " " + name + " " + sex + " " + age);
}
}
我们获取到ResultSet后,就可以将其转换为程序中的JAVA对象进行数据展示,但是原生的操作非常繁琐,所以Mybatis提供了ResultSet处理器,我们只需要定义好返回类型,Mybatis就可以自动进行转换映射了。
2.2 ResultSetHandler
ResultSetHandler是一个接口,也只有一个实现类,ResultSetHandler定义了一些处理结果集的方法。
ResultSetHandler接口源码:
/**
* @author Clinton Begin
* 结果集处理器
*/
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;
}
2.3 DefaultResultSetHandler
DefaultResultSetHandler是默认的结果集处理器,其中实现了很多处理一对一、一对多、嵌套查询等结果集的处理方法。
3. 执行流程
3.1 创建 ResultSetHandler
创建StatementHandler时,构造方法进入BaseStatementHandler,调用configuration的方法开始创建:
之后会调用
DefaultResultSetHandler的构造方法,并且也使用了拦截器包装。
DefaultResultSetHandler构造方法中,也设置了执行器、configuration、boundSql等重要参数,其中有一个reflectorFactory反射工厂,因为转为我们需要的对象是通过反射机制来创建的。
3.2 结果集处理
执行了查询之后,数据库的结果集就设置到了Statement对象中,接下来就是需要把Statement中的数据转换处理了。
首先调用DefaultResultSetHandler的handleResultSets方法处理Statement。
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
//结果集的第一个结果
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
//根据resultMap处理rsw生成java对象
handleResultSet(rsw, resultMap, multipleResults, null);
//获取结果集的下一个结果
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
//和resultMaps的遍历处理类似
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
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));
}
}
下一步通过
mappedStatement获取我们配置的ResultMap,ResultMap包含了当前数据库查询结果和我们需要的Java对象之间的映射关系:
handleResultSets方法中,最重要的一步就是handleResultSet方法,在这里会完成结果集数据处理:
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
// 存在父级映射
if (parentMapping != null) {
// 处理数据
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
// 创建结果处理器
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// 处理数据
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
// 多个结果
multipleResults.add(defaultResultHandler.getResultList());
} else {
// 处理数据
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
handleResultSet方法中最重要的就是handleRowValues方法,该方法中,简单的列表查询会进入到handleRowValuesForSimpleResultMap方法,循环处理每一条ResultSet结果集:
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
skipRows(resultSet, rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
handleRowValuesForSimpleResultMap方法中使用getRowValue获取行数据,在该方法中又会调用createResultObject方法创建结果对象:
createResultObject进入到重载的另外一个createResultObject方法中:
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException {
// 返回的对象类型
final Class<?> resultType = resultMap.getType();
// Class 元数据
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
// 结果集映射关系
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
if (hasTypeHandlerForResultObject(rsw, resultType)) {
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
// 返回类型为接口 或者 有默认的构造函数
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
这里进入到DefaultObjectFactory对象工厂的创建对象方法中,可以看到是通过反射机制调用构造函数实例化对象:
此时
createResultObject方法返回的对象还没有进行属性设置:
还需要
getRowValue方法进行属性映射,最后返回了我们想要的结果: