MyBatis会将结果集按照映射配置文件中定义的映射规则,例如<resultType>节点、resultType属性等,映射成相应的结果对象。这种映射机制就是MyBatis的核心功能之一,可以避免重复的JDBC代码。
在StatementHandler接口在执行完指定的select语句后,会将查询得到的结果集交给ResultSetHandler完成映射处理。ResultSetHandler除了负责映射select语句查询得到的及结果集,还会处理存储过程执行后的输出参数。
ResultSetHandler接口定义如下:
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是ResultSetHandler接口的唯一实现。DefaultResultSetHandler的核心字段含义如下:
private final Configuration configuration;
private final MappedStatement mappedStatement;
private final RowBounds rowBounds;
private final ParameterHandler parameterHandler;
private final ResultHandler<?> resultHandler;
private final BoundSql boundSql;
// TypeHandler注册中心
private final TypeHandlerRegistry typeHandlerRegistry;
// 对象工厂
private final ObjectFactory objectFactory;
// 反射工厂
private final ReflectorFactory reflectorFactory;
handleResultSets()方法
通过select语句查询数据库得到的结果集由DefaultResultSetHandler.handleResultSets()方法进行处理,该方法不仅可以处理Statement、PreparedStatement产生的结果集,还可以处理CallbaleStatement代用存储过程产生的多结果集。
DefaultResultSetHandler.handleResultSets()方法的具体如下:
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
//用于保存结果集对象
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
//statment可能返回多个结果集对象,这里先取出第一个结果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
//获取结果集对应resultMap,本质就是获取字段与java属性的映射规则
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);//结果集和resultMap不能为空,为空抛出异常
while (rsw != null && resultMapCount > resultSetCount) {
//获取当前结果集对应的resultMap
ResultMap resultMap = resultMaps.get(resultSetCount);
//根据映射规则(resultMap)对结果集进行转化,转换成目标对象以后放入multipleResults中
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);//获取下一个结果集
cleanUpAfterHandlingResultSet();//清空nestedResultObjects对象
resultSetCount++;
}
//获取多结果集。多结果集一般出现在存储过程的执行,存储过程返回多个resultset,
//mappedStatement.resultSets属性列出多个结果集的名称,用逗号分割;
//多结果集的处理不是重点,暂时不分析
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
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);
}
首先来看getFirstResultSet()、getNextResultSet()方法的实现,这两个方法都是JDBC处理多结果集的相关操作。
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
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)
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;
}
private ResultSetWrapper getNextResultSet(Statement stmt) {
// Making this method tolerant of bad JDBC drivers
try {
if (stmt.getConnection().getMetaData().supportsMultipleResultSets()) {
// Crazy Standard JDBC way of determining if there are more results
if (!(!stmt.getMoreResults() && stmt.getUpdateCount() == -1)) {
ResultSet rs = stmt.getResultSet();
if (rs == null) {
return getNextResultSet(stmt);
} else {
return new ResultSetWrapper(rs, configuration);
}
}
}
} catch (Exception e) {
// Intentionally ignored.
}
return null;
}
ResultSetWrapper
上一小节对DefaultResultSetHandler.getFirstResultSet()方法的分析中,可以看到DefaultResultSetHandler在获取ResultSet对象之后,会将其封装成ResultSetWrapper对象再进行处理。ResultSetWrapper是对ResultSet的一层包装,记录了ResultSet的一些元数据,并提供了一系列操作ResultSet的辅助方法。
网友评论