第六章
已经讲了语句的处理,本章开始讲下针对结果集的处理,从6.3
和6.4
中得到,结果集的入口方法是同一个,都是 resultSetHandler.handleResultSets ,ResultSetHandler 是一个接口类,在Mybatis中,只有一个实现类 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;
// 内部通过jdbc方式,拿到ResultSet,并包装成Mybatis中的ResultSetWrapper
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 语句的ResultMap集合,多个 ResultMap 配置时通过","分隔
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size(); // ResultMap 集数量
validateResultMapsCount(rsw, resultMapCount); // 验证 必须是 >= 1的
while (rsw != null && resultMapCount > resultSetCount) { // 遍历
// 取出 resultMap
ResultMap resultMap = resultMaps.get(resultSetCount);
// 真正处理结果集的方法,看章节`7.1`
handleResultSet(rsw, resultMap, multipleResults, null);
// 这个方法只有在支持多结果时,才会有值,否则返回 null
rsw = getNextResultSet(stmt);
// 清空处理过程中产生的未处理完的,其实就是有异常,没法处理的
cleanUpAfterHandlingResultSet();
// 自增1
resultSetCount++;
}
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);
}
// 这个方法里面都是jdbc的操作,建议先了解下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;
}
}
}
// rs 不为 null 的时候,直接用 ResultSetWrapper 包装类封装一下
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;
// 结果集的元数据(一般有列的数量、字段信息、类型等),想了解更多,可以去看jdbc相关API
final ResultSetMetaData metaData = rs.getMetaData();
// 查出的列数量
final int columnCount = metaData.getColumnCount();
// 遍历列
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));
}
}
7.1 handleResultSet
// 这个方法有多个if...else分支,但是不管怎么样,最终都会走到 handleRowValues 方法,继续探路
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());
}
}
handleRowValues
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
// 判断是否有嵌套 resultMap,为了讲解方便,假定没有
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
handleRowValuesForSimpleResultMap
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); // 跳过不要的行
// 跳过 offset 的行数后,如果后面还有数据,就取出来自己要的行数
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) { // 移动游标,一行行找
/* discriminator – 使用结果值来决定使用哪个 resultMap
case – 基于某些值的结果映射
嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
*/
// discriminator 的处理,这里不做扩展
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
// 取出每一行的值,并生成返回类型对象
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
// 将结果存储到 resultHandler 的定义的容器中,默认 DefaultResultHandler 是存放到 List 中,这也就是为什么在SqlSession底层,哪怕查询一条数据,也是走的selectList方法,ResultHandler 是一个接口类,可以自行扩展
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
skipRows
这就类似分页操作,并且是基于内存的分页,与之对应的是基于物理的分页,也就是在sql中增加limit来实现
private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
rs.absolute(rowBounds.getOffset()); // 跳转到设置的起始偏移行
}
} else {
for (int i = 0; i < rowBounds.getOffset(); i++) {
if (!rs.next()) {
break;
}
}
}
}
截止目前,关于xml方式的流程已经完结,这一章写的有点草率,主要原因是本人感冒了,有点写不下去了,但是主流流程还是没有缺失的,只是一些细节方法没有走进去讲,将就着看吧,有兴趣的读者可以自行扩展,或者评论区讨论下哪里需要深入讲解的,如有需要后续再补充。