网站外包公司该如何运营,做图片的网站,c 网站开发实例教程,清远网站制作公司MyBatis的SQL执行过程 在前面一系列的文档中#xff0c;我已经分析了 MyBatis 的基础支持层以及整个的初始化过程#xff0c;此时 MyBatis 已经处于就绪状态了#xff0c;等待使用者发号施令了 那么接下来我们来看看它执行SQL的整个过程#xff0c;该过程比较复杂#xff… MyBatis的SQL执行过程 在前面一系列的文档中我已经分析了 MyBatis 的基础支持层以及整个的初始化过程此时 MyBatis 已经处于就绪状态了等待使用者发号施令了 那么接下来我们来看看它执行SQL的整个过程该过程比较复杂涉及到二级缓存将返回结果转换成 Java 对象以及延迟加载等等处理过程这里将一步一步地进行分析 《SQL执行过程(一)之Executor》 《SQL执行过程(二)之StatementHandler》 《SQL执行过程(三)之ResultSetHandler》 《SQL执行过程(四)之延迟加载》 MyBatis中SQL执行的整体过程如下图所示 在 SqlSession 中会将执行 SQL 的过程交由Executor执行器去执行过程大致如下 通过DefaultSqlSessionFactory创建与数据库交互的 SqlSession “会话”其内部会创建一个Executor执行器对象然后Executor执行器通过StatementHandler创建对应的java.sql.Statement对象并通过ParameterHandler设置参数然后执行数据库相关操作如果是数据库更新操作则可能需要通过KeyGenerator先设置自增键然后返回受影响的行数如果是数据库查询操作则需要将数据库返回的ResultSet结果集对象包装成ResultSetWrapper然后通过DefaultResultSetHandler对结果集进行映射最后返回 Java 对象 上面还涉及到一级缓存、二级缓存和延迟加载等其他处理过程 SQL执行过程(三)之ResultSetHandler 高能预警 ❗ ❗ ❗ ❗ ❗ ❗ DefaultResultSetHandler(结果集处理器)将数据库查询结果转换成 Java 对象是一个非常繁琐的过程需要处理各种场景如果继续往下看请做好心理准备 可以先跳转到 DefaultResultSetHandler查看流程图 在前面SQL执行过程一系列的文档中已经详细地分析了在MyBatis的SQL执行过程中SqlSession会话将数据库操作交由Executor执行器去完成然后通过StatementHandler去执行数据库相关操作并获取到数据库的执行结果 如果是数据库查询操作则需要通过ResultSetHandler对查询返回的结果集进行映射处理转换成对应的Java对象算是SQL执行过程的最后一步那么我们来看看MyBatis是如何完成这个繁杂的解析过程的 ResultSetHandler接口的实现类如下图所示 先回顾一下ResultSetHandler在哪被调用在PreparedStatementHandler的query方法中代码如下 登录后复制 Override
public E ListE query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps (PreparedStatement) statement;// 执行ps.execute();// 结果处理器并返回结果return resultSetHandler.handleResultSets(ps);
} 1.2.3.4.5.6.7.8. 属性resultSetHandler默认为DefaultResultSetHandler对象可以回到 《SQL执行过程(二)之StatementHandler》的BaseStatementHandler小节中的构造方法的第3步可以看到调用resultSetHandler的handleResultSets(Statement stmt)方法对结果集进行映射转换成Java对象并返回 ResultSetWrapper 因为在DefaultResultSetHandler中对ResultSet的操作更多的是它的ResultSetWrapper包装类所以我们先来看看这个类 org.apache.ibatis.executor.resultset.ResultSetWrapperjava.sql.ResultSet的包装类为DefaultResultSetHandler提供许多便捷的方法直接来看它的代码 构造方法 登录后复制 public class ResultSetWrapper {/*** ResultSet 对象*/private final ResultSet resultSet;/*** 类型处理器注册表*/private final TypeHandlerRegistry typeHandlerRegistry;/*** ResultSet 中每列的列名*/private final ListString columnNames new ArrayList();/*** ResultSet 中每列对应的 Java Type*/private final ListString classNames new ArrayList();/*** ResultSet 中每列对应的 Jdbc Type*/private final ListJdbcType jdbcTypes new ArrayList();/*** 记录每列对应的 TypeHandler 对象* key列名* valueTypeHandler 集合*/private final MapString, MapClass?, TypeHandler? typeHandlerMap new HashMap();/*** 记录了被映射的列名* keyResultMap 对象的 id {link #getMapKey(ResultMap, String)}* valueResultMap 对象映射的列名集合*/private final MapString, ListString mappedColumnNamesMap new HashMap();/*** 记录了未映射的列名* keyResultMap 对象的 id {link #getMapKey(ResultMap, String)}* valueResultMap 对象未被映射的列名集合*/private final MapString, ListString unMappedColumnNamesMap new HashMap();public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {super();this.typeHandlerRegistry configuration.getTypeHandlerRegistry();this.resultSet rs;// 获取 ResultSet 的元信息final ResultSetMetaData metaData rs.getMetaData();final int columnCount metaData.getColumnCount();for (int i 1; i columnCount; i) {// 获得列名或者通过 AS 关键字指定列名的别名columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));// 获得该列对应的 Jdbc TypejdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));// 获得该列对应的 Java TypeclassNames.add(metaData.getColumnClassName(i));}}
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58. resultSet被包装的ResultSet结果集对象typeHandlerRegistry类型处理器注册表因为需要进行Java Type与Jdbc Type之间的转换columnNames结果集中的所有列名classNames结果集中的每列的对应的Java Type的名称jdbcTypes结果集中的每列对应的Jdbc TypetypeHandlerMap结果集中每列对应的类型处理器mappedColumnNamesMap保存每个ResultMap对象中映射的列名集合也就是我们在resultMap /标签下的子标签配置的column属性unMappedColumnNamesMap保存每个ResultMap对象中未映射的列名集合也就是没有在resultMap /标签下配置过但是查询结果返回了 在构造方法中会初始化上面的columnNames、classNames和jdbcTypes属性 getTypeHandler方法 getTypeHandler(Class? propertyType, String columnName)通过列名和Java Type获取对应的TypeHandler类型处理器方法如下 登录后复制 public TypeHandler? getTypeHandler(Class? propertyType, String columnName) {TypeHandler? handler null;// 获取列名对应的类型处理器MapClass?, TypeHandler? columnHandlers typeHandlerMap.get(columnName);if (columnHandlers null) {columnHandlers new HashMap();typeHandlerMap.put(columnName, columnHandlers);} else {handler columnHandlers.get(propertyType);}if (handler null) {// 获取该列对应的 Jdbc TypeJdbcType jdbcType getJdbcType(columnName);// 根据 Java Type 和 Jdbc Type 获取对应的 TypeHandler 类型处理器handler typeHandlerRegistry.getTypeHandler(propertyType, jdbcType);// Replicate logic of UnknownTypeHandler#resolveTypeHandler// See issue #59 comment 10if (handler null || handler instanceof UnknownTypeHandler) {// 从 ResultSet 中获取该列对应的 Java Type 的 Class 对象final int index columnNames.indexOf(columnName);final Class? javaType resolveClass(classNames.get(index));if (javaType ! null jdbcType ! null) {handler typeHandlerRegistry.getTypeHandler(javaType, jdbcType);} else if (javaType ! null) {handler typeHandlerRegistry.getTypeHandler(javaType);} else if (jdbcType ! null) {handler typeHandlerRegistry.getTypeHandler(jdbcType);}}if (handler null || handler instanceof UnknownTypeHandler) {// 最差的情况设置为 ObjectTypeHandlerhandler new ObjectTypeHandler();}// 将生成的 TypeHandler 存放在 typeHandlerMap 中columnHandlers.put(propertyType, handler);}return handler;
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38. 大致逻辑如下 先从MapString, MapClass?, TypeHandler? typeHandlerMap属性中获取类型处理器如果从缓存中没有获取到则尝试根据Jdbc Type和Java Type从typeHandlerRegistry注册表获取如果还是没有获取到则根据classNames中拿到结果集中该列的Java Type然后在从typeHandlerRegistry注册表获取还是没有获取到则设置为ObjectTypeHandler最后将其放入typeHandlerMap缓存中 loadMappedAndUnmappedColumnNames方法 loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix)方法初始化mappedColumnNamesMap和unMappedColumnNamesMap两个属性分别为映射的列名和未被映射的列名方法如下 登录后复制 private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {ListString mappedColumnNames new ArrayList();ListString unmappedColumnNames new ArrayList();// 1 获取配置的列名的前缀全部大写final String upperColumnPrefix columnPrefix null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);/** 2 获取 ResultMap 中配置的所有列名并添加前缀* 如果在 select / 上面配置的是 resultType 属性则返回的是空集合因为它生成的 ResultMap 只有 Java Type 属性*/final SetString mappedColumns prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);/** 3 遍历数据库查询结果中所有的列名* 将所有列名分为两类是否配置了映射*/for (String columnName : columnNames) {final String upperColumnName columnName.toUpperCase(Locale.ENGLISH);if (mappedColumns.contains(upperColumnName)) {mappedColumnNames.add(upperColumnName);} else {unmappedColumnNames.add(columnName);}}// 4 将上面两类的列名保存mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26. 获取配置的列名的前缀全部大写通常是没有配置的获取ResultMap中配置的所有列名并添加前缀如果在select /上面配置的是resultType属性则返回的是空集合因为它创建的ResultMap对象中只有Java Type属性遍历结果集中所有的列名如果在resultMap /标签中的子标签配置的column属性有包含这个列名则属于映射的列名否则就属于未被映射的列名 ResultSetHandler org.apache.ibatis.executor.resultset.ResultSetHandler结果集映射接口代码如下 登录后复制 public interface ResultSetHandler {/*** 处理 {link java.sql.ResultSet} 成映射的对应的结果** param stmt Statement 对象* param E 泛型* return 结果数组* throws SQLException SQL异常*/E ListE handleResultSets(Statement stmt) throws SQLException;/*** 处理 {link java.sql.ResultSet} 成 Cursor 对象** param stmt Statement 对象* param E 泛型* return Cursor 对象* throws SQLException SQL异常*/E CursorE handleCursorResultSets(Statement stmt) throws SQLException;/*** 暂时忽略和存储过程相关** param cs CallableStatement 对象* throws SQLException SQL异常*/void handleOutputParameters(CallableStatement cs) throws SQLException;
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29. DefaultResultSetHandler org.apache.ibatis.executor.resultset.DefaultResultSetHandler实现ResultSetHandler接口处理数据库的查询结果对结果集进行映射将结果转换成Java对象 由于该类嵌套的方法太多了可能一个方法会有十几层的嵌套所以本分不会进行全面的分析 因为我查看这个类的时候是从下面的方法一层一层往上看的注释我全部添加了所以可以参考我的注释一步一步查看 接下来的描述可能有点混乱请按照我在方法前面表明的顺序进行查看参考 DefaultResultSetHandler.java 先来看下DefaultResultSetHandler处理结果集的方法的流程图 构造方法 登录后复制 public class DefaultResultSetHandler implements ResultSetHandler {/*** 延迟加载默认对象*/private static final Object DEFERRED new Object();/*** 执行器*/private final Executor executor;/*** 全局配置对象*/private final Configuration configuration;/*** 本次查询操作对应的 MappedStatement 对象*/private final MappedStatement mappedStatement;/*** 分页对象*/private final RowBounds rowBounds;/*** 参数处理器默认为 DefaultParameterHandler*/private final ParameterHandler parameterHandler;/*** 结果处理器默认为 DefaultResultHandler*/private final ResultHandler? resultHandler;/*** SQL 相关信息*/private final BoundSql boundSql;/*** 类型处理器注册表*/private final TypeHandlerRegistry typeHandlerRegistry;/*** 对象实例工厂*/private final ObjectFactory objectFactory;/*** Reflector 工厂*/private final ReflectorFactory reflectorFactory;// nested resultmapsprivate final MapCacheKey, Object nestedResultObjects new HashMap();private final MapString, Object ancestorObjects new HashMap();private Object previousRowValue;// multiple resultsetsprivate final MapString, ResultMapping nextResultMaps new HashMap();private final MapCacheKey, ListPendingRelation pendingRelations new HashMap();// Cached Automappingsprivate final MapString, ListUnMappedColumnAutoMapping autoMappingsCache new HashMap();// temporary marking flag that indicate using constructor mapping (use field to reduce memory usage)private boolean useConstructorMappings;public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement,ParameterHandler parameterHandler, ResultHandler? resultHandler, BoundSql boundSql, RowBounds rowBounds) {this.executor executor;this.configuration mappedStatement.getConfiguration();this.mappedStatement mappedStatement;this.rowBounds rowBounds;this.parameterHandler parameterHandler;this.boundSql boundSql;this.typeHandlerRegistry configuration.getTypeHandlerRegistry();this.objectFactory configuration.getObjectFactory();this.reflectorFactory configuration.getReflectorFactory();this.resultHandler resultHandler;}
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75. 上面的属性有点多可以先根据注释进行理解也可以在接下来的方法中逐步理解 1.handleResultSets方法 handleResultSets(Statement stmt)方法处理结果集的入口 登录后复制 /*** 1.处理结果集*/
Override
public ListObject handleResultSets(Statement stmt) throws SQLException {ErrorContext.instance().activity(handling results).object(mappedStatement.getId());/** 1 用于保存映射结果集得到的结果队形* 多 ResultSet 的结果集合每个 ResultSet 对应一个 Object 对象而实际上每个 Object 是 ListObject 对象*/final ListObject multipleResults new ArrayList();int resultSetCount 0;// 2 获取 ResultSet 对象并封装成 ResultSetWrapperResultSetWrapper rsw getFirstResultSet(stmt);/** 3 获得当前 MappedStatement 对象中的 ResultMap 集合XML 映射文件中 resultMap / 标签生成的* 或者 配置 resultType 属性也会生成对应的 ResultMap 对象* 在 select / 标签配置 ResultMap 属性时可以以逗号分隔配置多个如果返回多个 ResultSet 则会一一映射通常配置一个*/ListResultMap resultMaps mappedStatement.getResultMaps();int resultMapCount resultMaps.size();// 4 如果有返回结果但是没有 ResultMap 接收对象则抛出异常validateResultMapsCount(rsw, resultMapCount);while (rsw ! null resultMapCount resultSetCount) {ResultMap resultMap resultMaps.get(resultSetCount);/** 5 完成结果集的映射全部转换的 Java 对象* 保存至 multipleResults 集合中或者 this.resultHandler 中*/handleResultSet(rsw, resultMap, multipleResults, null);// 获取下一个结果集rsw getNextResultSet(stmt);// 清空 nestedResultObjects 集合cleanUpAfterHandlingResultSet();// 递增 resultSetCount 结果集数量resultSetCount;}// 6 获取 resultSets 多结果集属性的配置存储过程中使用暂时忽略String[] resultSets mappedStatement.getResultSets();if (resultSets ! null) {while (rsw ! null resultSetCount resultSets.length) {// 根据 resultSet 的名称获取未处理的 ResultMappingResultMapping parentMapping nextResultMaps.get(resultSets[resultSetCount]);if (parentMapping ! null) {String nestedResultMapId parentMapping.getNestedResultMapId();// 未处理的 ResultMap 对象ResultMap resultMap configuration.getResultMap(nestedResultMapId);// 完成结果集的映射全部转换的 Java 对象handleResultSet(rsw, resultMap, null, parentMapping);}// 获取下一个结果集rsw getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount;}}// 7 如果是 multipleResults 单元素则取首元素返回return collapseSingleResultList(multipleResults);
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64. multipleResults用于保存映射结果集得到的结果队形多 ResultSet 的结果集合每个 ResultSet 对应一个 Object 对象而实际上每个 Object 是 ListObject 对象获取 ResultSet 对象并封装成 ResultSetWrapper获得当前 MappedStatement 对象中的 ResultMap 集合XML 映射文件中resultMap /标签生成的或者 配置 resultType 属性也会生成对应的 ResultMap 对象在 select / 标签配置 ResultMap 属性时可以以逗号分隔配置多个如果返回多个 ResultSet 则会一一映射通常配置一个如果有返回结果但是没有 ResultMap 接收对象则抛出异常调用handleResultSet方法完成结果集的映射全部转换的 Java 对象保存至 multipleResults 集合中或者 this.resultHandler 中(用户自定的通常不会)获取 resultSets 多结果集属性的配置存储过程中使用暂时忽略本文暂不分析 完成结果集映射的任务还是交给了2.handleResultSet方法 2.handleResultSet方法 handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, ListObject multipleResults, ResultMapping parentMapping)方法处理结果集 登录后复制 private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, ListObject multipleResults,ResultMapping parentMapping) throws SQLException {try {if (parentMapping ! null) {// 1 暂时忽略因为只有存储过程的情况时 parentMapping 为非空handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);} else {if (resultHandler null) { // 2// 2.1 创建 DefaultResultHandler 默认结果处理器DefaultResultHandler defaultResultHandler new DefaultResultHandler(objectFactory);// 2.2 处理结果集进行一系列的处理完成映射将结果保存至 DefaultResultHandler 中handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);// 2.3 将结果集合添加至 multipleResults 中multipleResults.add(defaultResultHandler.getResultList());} else { // 用户自定义了 resultHandler则结果都会保存在其中// 3 处理结果集进行一系列的处理完成映射将结果保存至 DefaultResultHandler 中handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);}}} finally {// issue #228 (close resultsets)// 4 关闭结果集closeResultSet(rsw.getResultSet());}
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25. 暂时忽略因为只有存储过程的情况时 parentMapping 为非空查看上面的1.handleResultSets方法的第6步用户没有指定ResultHandler结果处理器 创建DefaultResultHandler默认结果处理器就是使用一个List集合保存转换后的Java对象调用handleRowValues方法处理结果集进行一系列的处理完成映射将结果保存至 DefaultResultHandler 中将结果集合添加至 multipleResults 中 用户指定了自定义的ResultHandler结果处理器和第2步的区别在于处理后的Java对象不会保存在multipleResults 中仅保存在ResultHandler中用户可通过它获取关闭 ResultSet 结果集对象 通常我们不会自定义结果处理器的所以第4步本文暂不分析我们来看到第2步最终还是交给了3.handleRowValues方法 3.handleRowValues方法 handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler? resultHandler, RowBounds rowBounds, ResultMapping parentMapping)方法处理结果集 登录后复制 public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler? resultHandler,RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {/** 1 ResultMap 存在内嵌的 ResultMap* 例如 resultMap / 标签中 association / 或者 collection / 都会创建对应的 ResultMap 对象* 该对象的 id 会设置到 ResultMapping 的 nestedResultMapId 属性中这就属于内嵌的 ResultMap*/if (resultMap.hasNestedResultMaps()) { // 存在// 1.1 如果不允许在嵌套语句中使用分页则对 rowBounds 进行校验设置了 limit 或者 offset 则抛出异常默认允许ensureNoRowBounds();// 1.2 校验要不要使用自定义的 ResultHandler针对内嵌的 ResultMapcheckResultHandler();// 1.3 处理结果集进行映射生成返回结果保存至 resultHandler 或者设置到 parentMapping 的对应属性中// 这里会处理内嵌的 ResultMaphandleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);} else {// 2 处理结果集进行映射生成返回结果保存至 resultHandler 或者设置到 parentMapping 的对应属性中handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);}
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20. 如果当前 ResultMap 存在内嵌的 ResultMap例如 resultMap / 标签中 association / 或者 collection / 都会创建对应的 ResultMap 对象该对象的 id 会设置到 ResultMapping 的 nestedResultMapId 属性中这就属于内嵌的 ResultMap 如果不允许在嵌套语句中使用分页则对 rowBounds 进行校验设置了 limit 或者 offset 则抛出异常默认允许校验要不要使用自定义的 ResultHandler针对内嵌的 ResultMap处理结果集进行映射生成返回结果保存至 resultHandler 或者设置到 parentMapping(存储过程相关本文暂不分析)的对应属性中这里会对内嵌的 ResultMap 进行处理调用handleRowValuesForNestedResultMap方法 处理结果集进行映射生成返回结果保存至 resultHandler 或者设置到 parentMapping(存储过程相关本文暂不分析)的对应属性中调用handleRowValuesForSimpleResultMap方法 这里先来看到第2步中的4.handleRowValuesForSimpleResultMap方法因为这个处理的情况相比第1步调用的方法简单些 4.handleRowValuesForSimpleResultMap方法 handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler? resultHandler, RowBounds rowBounds, ResultMapping parentMapping)方法处理结果集(不含嵌套映射) 登录后复制 private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler? resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {// 默认的上下文对象临时保存每一行的结果且记录返回结果数量DefaultResultContextObject resultContext new DefaultResultContext();ResultSet resultSet rsw.getResultSet();// 1 根据 RowBounds 中的 offset 跳到到指定的记录skipRows(resultSet, rowBounds);// 2 检测已经处理的行数是否已经达到上限(RowBounds.limit)以及 ResultSet 中是否还有可处理的记录while (shouldProcessMoreRows(resultContext, rowBounds) !resultSet.isClosed() resultSet.next()) {/** 3 获取最终的 ResultMap* 因为 ResultMap 可能使用到了 discriminator / 标签需要根据不同的值映射不同的 ResultMap* 如果存在 Discriminator 鉴别器则根据当前记录选择对应的 ResultMap会一直嵌套处理*/ResultMap discriminatedResultMap resolveDiscriminatedResultMap(resultSet, resultMap, null);// 4 从结果集中获取到返回结果对象进行映射比较复杂关键方法Object rowValue getRowValue(rsw, discriminatedResultMap, null);// 5 将返回结果对象保存至 resultHandler或者设置到父对象 parentMapping 的对应属性中storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);}
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22. 这里创建了一个DefaultResultContext保存结果的上下文对象点击去你会发现有3个属性 resultObject暂存映射后的返回结果因为结果集中可能有很多条数据resultCount记录经过 DefaultResultContext 暂存的对象个数stopped控制是否还进行映射 根据 RowBounds 中的 offset 跳到到结果集中指定的记录检测已经处理的行数是否已经达到上限(RowBounds.limit)以及 ResultSet 中是否还有可处理的记录调用resolveDiscriminatedResultMap方法获取最终的 ResultMap因为 ResultMap 可能使用到了 discriminator / 标签需要根据不同的值映射不同的 ResultMap如果存在 Discriminator 鉴别器则根据当前记录选择对应的 ResultMap会一直嵌套处理调用getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix)方法从结果集中获取到返回结果对象进行映射比较复杂关键方法调用storeObject方法将返回结果对象保存至 resultHandler或者设置到父对象 parentMapping(存储过程相关本文暂不分析)的对应属性中 对于第3、4、5步的三个方法我们一个一个来看 4.1resolveDiscriminatedResultMap方法4.2getRowValue方法4.3storeObject方法 4.1resolveDiscriminatedResultMap方法 resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix)方法如果存在discriminator /鉴别器则进行处理选择对应的 ResultMap会一直嵌套处理 登录后复制 public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix)throws SQLException {// 记录已经处理过的 ResultMap 的 idSetString pastDiscriminators new HashSet();// 1 获取 ResultMap 中的 Discriminator 鉴别器discriminator /标签会被解析成该对象Discriminator discriminator resultMap.getDiscriminator();while (discriminator ! null) {// 2 获取当前记录中该列的值通过类型处理器转换成了对应的类型final Object value getDiscriminatorValue(rs, discriminator, columnPrefix);// 3 鉴别器根据该值获取到对应的 ResultMap 的 idfinal String discriminatedMapId discriminator.getMapIdFor(String.valueOf(value));if (configuration.hasResultMap(discriminatedMapId)) {// 3.1 获取到对应的 ResultMapresultMap configuration.getResultMap(discriminatedMapId);// 3.2 记录上一次的鉴别器Discriminator lastDiscriminator discriminator;// 3.3 获取到对应 ResultMap 内的鉴别器可能鉴别器里面还有鉴别器discriminator resultMap.getDiscriminator();// 3.4 检测是否出现循环嵌套了if (discriminator lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {break;}} else {// 4 鉴别结果没有对应的 ResultMap则直接跳过break;}}// 5 返回最终使用的 ResultMap 对象return resultMap;
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30. 获取 ResultMap 中的 Discriminator 鉴别器discriminator / 标签会被解析成该对象调用getDiscriminatorValue方法获取当前记录中该列的值通过类型处理器转换成了对应的类型方法如下 登录后复制 private Object getDiscriminatorValue(ResultSet rs, Discriminator discriminator, String columnPrefix) throws SQLException {// 获取 discriminator /标签对应的的 ResultMapping 对象final ResultMapping resultMapping discriminator.getResultMapping();// 获取 TypeHandler 类型处理器final TypeHandler? typeHandler resultMapping.getTypeHandler();// 通过 TypeHandler 从 ResultSet 中获取该列的值return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));
} 1.2.3.4.5.6.7.8. Discriminator 鉴别器根据该值获取到对应的 ResultMap 的 id 存在对应的 ResultMap 对象则获取到记录上一次的鉴别器获取到对应 ResultMap 内的鉴别器可能鉴别器里面还有鉴别器检测是否出现循环嵌套了 Discriminator 鉴别结果没有对应的 ResultMap则直接跳过返回最终使用的 ResultMap 对象 4.2getRowValue方法 getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix)方法处理结果集 登录后复制 private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {// 1 保存延迟加载的集合final ResultLoaderMap lazyLoader new ResultLoaderMap();// 2 创建返回结果的实例对象(如果存在嵌套子查询且是延迟加载则为其创建代理对象后续的延迟加载保存至 lazyLoader 中即可)Object rowValue createResultObject(rsw, resultMap, lazyLoader, columnPrefix);/** 3 如果上面创建的返回结果的实例对象不为 null并且没有对应的 TypeHandler 类型处理器则需要对它进行赋值* 例如我们返回结果为 java.lang.String 就不用了因为上面已经处理且赋值了*/if (rowValue ! null !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {// 3.1 将返回结果的实例对象封装成 MetaObject便于操作final MetaObject metaObject configuration.newMetaObject(rowValue);// 3.2 标记是否成功映射了任意一个属性useConstructorMappings 表示是否在构造方法中使用了参数映射boolean foundValues this.useConstructorMappings;// 3.3 检测是否需要自动映射if (shouldApplyAutomaticMappings(resultMap, false)) {/** 3.4 从结果集中将未被映射的列值设置到返回结果 metaObject 中* 返回是否映射成功设置了1个或以上的属性值*/foundValues applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;}/** 3.5 从结果集中将 ResultMap 中需要映射的列值设置到返回结果 metaObject 中* 返回是否映射成功设置了1个或以上的属性值*/foundValues applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;foundValues lazyLoader.size() 0 || foundValues;/** 3.6 如果没有成功映射任意一个属性则根据 returnInstanceForEmptyRow 全局配置(默认为false)返回空对象还是 null*/rowValue foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;}// 4 返回该结果对象return rowValue;
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37. 创建一个保存延迟加载的集合ResultLoaderMap对象lazyLoader如果存在代理对象创建的代理对象则需要通过它来执行需要延迟加载的方法在后续会将到调用createResultObject方法创建返回结果的实例对象rowValue(如果存在嵌套子查询且是延迟加载则为其创建代理对象后续的延迟加载保存至 lazyLoader 中即可)如果上面创建的返回结果的实例对象rowValue不为 null并且没有对应的 TypeHandler 类型处理器则需要对它进行赋值例如我们返回结果为 java.lang.String 就不用了因为上面已经处理且赋值了 将返回结果的实例对象封装成 MetaObject 对象metaObject便于操作标记是否成功映射了任意一个属性useConstructorMappings 表示是否在构造方法中使用了参数映射调用shouldApplyAutomaticMappings方法检测是否需要自动映射就是对未被映射的列进行处理调用applyAutomaticMappings方法从结果集中将未被映射的列值设置到返回结果 metaObject 中返回是否映射成功(设置了1个或以上的属性值)调用applyPropertyMappings方法从结果集中将 ResultMap 中需要映射的列值设置到返回结果 metaObject 中返回是否映射成功(设置了1个或以上的属性值)如果没有成功映射任意一个属性则根据 returnInstanceForEmptyRow 全局配置(默认为false)返回空对象还是 null 返回该结果对象rowValue 我们逐步来看上面的第2、3.3、3.4、3.5所调用的方法 4.2.1createResultObject方法4.2.2shouldApplyAutomaticMappings方法4.2.3applyAutomaticMappings方法4.2.4applyPropertyMappings方法 4.2.1createResultObject方法 createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix)方法创建返回结果的实例对象(如果存在嵌套子查询且是延迟加载则为其创建代理对象) 登录后复制 private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader,String columnPrefix) throws SQLException {// 标记构造方法中是否使用了参数映射this.useConstructorMappings false; // reset previous mapping result// 1 记录构造方法的入参类型final ListClass? constructorArgTypes new ArrayList();// 2 记录构造方法的参数值final ListObject constructorArgs new ArrayList();// 3 创建返回结果的实例对象该步骤的核心Object resultObject createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);/** 4 如果返回结果的实例对象不为空且返回结果没有对应的 TypeHandler 类型处理器* 则遍历所有的映射列如果存在嵌套子查询并且要求延迟加载那么为该返回结果的实例对象创建一个动态代理对象(Javassist)* 这样一来可以后续将需要延迟加载的属性放入 lazyLoader 中即可** 为该对象创建对应的代理对象其中通过 ResultLoaderMap 对延迟加载的方法进行了增强* 调用 getter 方法时执行查询并从 ResultLoaderMap 中删除直接调用 setter 方法也会从中删除*/if (resultObject ! null !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {final ListResultMapping propertyMappings resultMap.getPropertyResultMappings();for (ResultMapping propertyMapping : propertyMappings) {// issue gcode #109 issue #149if (propertyMapping.getNestedQueryId() ! null propertyMapping.isLazy()) {resultObject configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration,objectFactory, constructorArgTypes, constructorArgs);break;}}}// 5 记录是否使用有参构造方法创建的该返回结果实例对象this.useConstructorMappings resultObject ! null !constructorArgTypes.isEmpty(); // set current mapping resultreturn resultObject;
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33. 记录构造方法的入参类型记录构造方法的参数值调用createResultObject方法(重载)创建返回结果的实例对象该步骤的核心如果返回结果的实例对象不为空且返回结果没有对应的 TypeHandler 类型处理器例如一个实体类则遍历所有的映射列如果存在嵌套子查询并且要求延迟加载那么为该返回结果的实例对象创建一个动态代理对象(Javassist)这样一来可以后续将需要延迟加载的属性放入 lazyLoader 中即可在后续会讲到记录是否使用有参构造方法创建的该返回结果实例对象就是使用了映射后续判断返回空对象还是null需要用到返回实例对象也可能是它的动态代理对象 这里我们需要来看到第3步调用的createResultObject重载方法 4.2.1.1createResultObject重载方法 4.2.1.1createResultObject重载方法 createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ListClass? constructorArgTypes, ListObject constructorArgs, String columnPrefix)方法找到构造方法创建一个实例对象 登录后复制 private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ListClass? constructorArgTypes,ListObject constructorArgs, String columnPrefix) throws SQLException {// 获取 Java Typefinal Class? resultType resultMap.getType();// 创建对应的 MetaClass 对象便于操作final MetaClass metaType MetaClass.forClass(resultType, reflectorFactory);// 获取 constructor / 标签下构造函数的入参信息可以通过这些入参确认一个构造函数final ListResultMapping constructorMappings resultMap.getConstructorResultMappings();/** 创建结果对象分为下面4种场景* 1. 结果集只有一列且存在对应的 TypeHandler 类型处理器例如返回 java.lang.String* 2. resultMap / 标签下配置的 constructor / 标签下的构造函数参数信息不为空* 3. 返回类型为接口或者有默认的构造方法* 4. 找到合适的构造方法*/if (hasTypeHandlerForResultObject(rsw, resultType)) { // 场景1// 将该列转换成对应 Java Type 的值然后返回return createPrimitiveResultObject(rsw, resultMap, columnPrefix);} else if (!constructorMappings.isEmpty()) { // 场景2// 根据 constructor / 标签下的构造方法入参配置尝试从结果集中获取入参值并创建返回结果的实例对象return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) { // 场景3// 使用默认无参构造方法创建返回结果的实例对象return objectFactory.create(resultType);} else if (shouldApplyAutomaticMappings(resultMap, false)) { // 场景4// 找到合适的构造方法并创建返回结果对象return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);}throw new ExecutorException(Do not know how to create an instance of resultType);
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31. 创建结果对象依次分为下面4种场景 结果集只有一列且存在对应的 TypeHandler 类型处理器例如返回 java.lang.String则调用createPrimitiveResultObject方法将该列转换成对应 Java Type 的值然后返回resultMap / 标签下配置的 constructor / 标签下的构造函数参数信息不为空则调用createParameterizedResultObject方法根据 constructor / 标签下的构造方法入参配置尝试从结果集中获取入参值并创建返回结果的实例对象返回类型为接口或者有默认的构造方法则通过实例工厂objectFactory使用默认无参构造方法创建返回结果的实例对象找到合适的构造方法则调用createByConstructorSignature方法找到合适的构造方法并创建返回结果对象 好的接下来我们又要看到第1、2、4步调用的三个方法了 4.2.1.2createPrimitiveResultObject方法4.2.1.3createParameterizedResultObject方法4.2.1.4createByConstructorSignature方法 4.2.1.2createPrimitiveResultObject方法 createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix)方法创建返回结果实例对象(通常是Java定义的类型例如java.lang.String) 登录后复制 private Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix)throws SQLException {// 获取 Java Typefinal Class? resultType resultMap.getType();final String columnName;/** 获取列名*/if (!resultMap.getResultMappings().isEmpty()) { // 配置了 resultMap /// 获取 resultMap / 标签下的配置信息final ListResultMapping resultMappingList resultMap.getResultMappings();// 因为只有一个参数则直接取第一个final ResultMapping mapping resultMappingList.get(0);// 从配置中获取 column 属性columnName prependPrefix(mapping.getColumn(), columnPrefix);} else {// 从结果集中获取列名columnName rsw.getColumnNames().get(0);}// 通过 Java Type 和列名获取对应的 TypeHandlerfinal TypeHandler? typeHandler rsw.getTypeHandler(resultType, columnName);// 通过 TypeHandler 将返回结果转换成对应 Java Type 的值return typeHandler.getResult(rsw.getResultSet(), columnName);
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24. 通过ResultSetWrapper根据Java Type和columnName找到对应的TypeHandler类型处理器通过TypeHandler类型处理器将结果集中的结果转换成对应的 Java 对象 4.2.1.3createParameterizedResultObject方法 createParameterizedResultObject(ResultSetWrapper rsw, Class? resultType, ListResultMapping constructorMappings, ListClass? constructorArgTypes, ListObject constructorArgs, String columnPrefix)方法 根据 resultMap / 标签下的 constructor / 标签配置的参数构建一个实例对象 登录后复制 Object createParameterizedResultObject(ResultSetWrapper rsw, Class? resultType,ListResultMapping constructorMappings, ListClass? constructorArgTypes, ListObject constructorArgs,String columnPrefix) {// 标记是否找到配置的构造函数的所有入参boolean foundValues false;for (ResultMapping constructorMapping : constructorMappings) {// 获取参数的 Java Typefinal Class? parameterType constructorMapping.getJavaType();// 获取参数对应的 column 列名final String column constructorMapping.getColumn();final Object value;try {/** 获取该属性值可能存在以下几种场景* 1. 存在嵌套查询* 2. 存在嵌套 ResultMap* 3. 直接获取值*/if (constructorMapping.getNestedQueryId() ! null) { // 场景1// 通过嵌套查询获取到该属性值value getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);} else if (constructorMapping.getNestedResultMapId() ! null) { // 场景2// 获取到嵌套的 ResultMap 对象final ResultMap resultMap configuration.getResultMap(constructorMapping.getNestedResultMapId());// 从结果集中获取到嵌套 ResultMap 对应的值value getRowValue(rsw, resultMap, getColumnPrefix(columnPrefix, constructorMapping));} else { // 场景3final TypeHandler? typeHandler constructorMapping.getTypeHandler();// 通过 TypeHandler 从结果集中获取该列的值value typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));}} catch (ResultMapException | SQLException e) {throw new ExecutorException(Could not process result for mapping: constructorMapping, e);}constructorArgTypes.add(parameterType);constructorArgs.add(value);foundValues value ! null || foundValues;}// 如果构造函数的入参全部找到则创建返回结果的实例对象return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41. 需要先从结果集中获取每个constructor / 标签配置的参数对应的值这里又可能存在以下三种情况 该参数存在嵌套查询则调用getNestedQueryConstructorValue方法获取到该属性值存在嵌套 ResultMap则调用getRowValue方法从该结果集中获取到嵌套 ResultMap 对应的值回到了4.2getRowValue方法正常情况通过TypeHandler类型处理器根据列名从结果集中获取到该属性值 通过objectFactory实例工厂根据上面配置的入参信息构建一个实例对象 这里我们又要进入第1.1步的方法 4.2.1.3.1getNestedQueryConstructorValue方法 4.2.1.3.1getNestedQueryConstructorValue方法 getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix)方法处理构造方法的入参出现嵌套子查询这种情况获取该参数值 登录后复制 private Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix)throws SQLException {// 1 获得嵌套查询关联的 idfinal String nestedQueryId constructorMapping.getNestedQueryId();// 2 获取嵌套查询对应的 MappedStatement 对象final MappedStatement nestedQuery configuration.getMappedStatement(nestedQueryId);// 3 获取嵌套查询的参数类型final Class? nestedQueryParameterType nestedQuery.getParameterMap().getType();// 4 获取嵌套查询的参数对象已完成初始化final Object nestedQueryParameterObject prepareParameterForNestedQuery(rs, constructorMapping, nestedQueryParameterType, columnPrefix);Object value null;// 5 执行查询if (nestedQueryParameterObject ! null) {// 5.1 获取嵌套查询中的 SQL 对象final BoundSql nestedBoundSql nestedQuery.getBoundSql(nestedQueryParameterObject);// 5.2 获取CacheKey对象final CacheKey key executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);final Class? targetType constructorMapping.getJavaType();// 5.3 创建 ResultLoader 对象final ResultLoader resultLoader new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);// 5.4 加载结果value resultLoader.loadResult();}return value;
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26. 获得嵌套查询关联的 id获取嵌套查询对应的 MappedStatement 对象获取嵌套查询的参数类型获取嵌套查询的参数对象已完成初始化调用prepareParameterForNestedQuery方法进去后发现又得两层方法这里就不再展开了比较简单可以先参考的我的注释查看在后续还会调用该方法再进行解析执行查询因为这里的构造方法中的入参所以无需判断延迟加载在后面设置属性时就不一样了 获取嵌套查询中的 SQL 对象获取CacheKey对象创建 ResultLoader 对象加载结果 返回子查询返回的值 4.2.1.4createByConstructorSignature方法 createByConstructorSignature(ResultSetWrapper rsw, Class? resultType, ListClass? constructorArgTypes, ListObject constructorArgs)方法尝试找一个合适的构造方法构建一个实例对象 登录后复制 private Object createByConstructorSignature(ResultSetWrapper rsw, Class? resultType,ListClass? constructorArgTypes, ListObject constructorArgs) throws SQLException {// 1 获取所有的构造函数final Constructor?[] constructors resultType.getDeclaredConstructors();// 2 找到添加了 AutomapConstructor 注解的构造方法final Constructor? defaultConstructor findDefaultConstructor(constructors);if (defaultConstructor ! null) {// 使用这个构造方法创建返回结果的实例对象return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, defaultConstructor);} else {for (Constructor? constructor : constructors) { // 3 遍历所有的构造方法// 如果构造方法的入参与结果集中列的个数相同并且入参的 Java Type 和列的 Jdbc Type 有类型处理器if (allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {// 使用这个构造方法创建返回结果的实例对象return createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, constructor);}}}throw new ExecutorException(No constructor found in resultType.getName() matching rsw.getClassNames());
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20. 获取所有的构造函数找到添加了 AutomapConstructor 注解的构造方法如果存在则调用createUsingConstructor方法创建一个实例对象否则遍历所有的构造方法 如果构造方法的入参与结果集中列的个数相同并且入参的 Java Type 和列的 Jdbc Type 有类型处理器使用这个构造方法创建返回结果的实例对象调用createUsingConstructor方法创建一个实例对象 上面需要调用的createUsingConstructor方法比较简单这里就不再展开了大致逻辑就是从结果集中获取到该构造方法所有的入参然后构建一个实例对象 4.2.2shouldApplyAutomaticMappings方法 shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested)方法检测是否需要自动映射(对未被映射的列进行处理) 登录后复制 private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {/** 获取resultMap /中的 autoMapping 配置* 如果不为空则返回该值是否自定映射*/if (resultMap.getAutoMapping() ! null) {return resultMap.getAutoMapping();} else {/** 全局配置 AutoMappingBehavior 默认为 PARTIAL* 如果是嵌套这里默认就返回 false*/if (isNested) { // 嵌套映射return AutoMappingBehavior.FULL configuration.getAutoMappingBehavior();} else {return AutoMappingBehavior.NONE ! configuration.getAutoMappingBehavior();}}
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19. 如果resultMap /中的autoMapping配置不为空则返回该配置否则通过全局配置来判断默认PARTIAL也就是不是嵌套映射则需要对未被映射的列进行处理嵌套查询的话不会对未被映射的列进行处理(需要配置为FULL) 4.2.3applyAutomaticMappings方法 applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix)方法对未被映射的字段进行映射 登录后复制 private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,String columnPrefix) throws SQLException {// 1 将这些未被映射的字段创建对应的 UnMappedColumnAutoMapping 对象ListUnMappedColumnAutoMapping autoMapping createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);// 标记是否找到1个以上的属性值延迟加载也算boolean foundValues false;if (!autoMapping.isEmpty()) {// 2 遍历未被映射的字段数组将这些属性设置到返回结果对象中for (UnMappedColumnAutoMapping mapping : autoMapping) {// 2.1 通过 TypeHandler 获取未被映射的字段的值final Object value mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);if (value ! null) {foundValues true;}/** 2.2 如果属性值不为空或者配置了值为 null 也往返回结果设置该属性值(不能是基本类型)*/if (value ! null || (configuration.isCallSettersOnNulls() !mapping.primitive)) {// gcode issue #377, call setter on nulls (value is not found)// 往返回结果设置属性值metaObject.setValue(mapping.property, value);}}}return foundValues;
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26. 调用createAutomaticMappings方法将这些未被映射的字段创建对应的 UnMappedColumnAutoMapping 对象(包含列名、属性名、类型处理器、是否为原始类型)遍历未被映射的字段数组将这些属性设置到返回结果对象中 通过 TypeHandler 类型处理器获取未被映射的字段的值如果属性值不为空或者配置了值为 null 也往返回结果设置该属性值(不能是基本类型)则往返回结果中设置该属性值 这里我们来看到createAutomaticMappings方法 4.2.3.1createAutomaticMappings方法 4.2.3.1createAutomaticMappings方法 createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix)方法将这些未被映射的字段创建对应的 UnMappedColumnAutoMapping 对象 登录后复制 private ListUnMappedColumnAutoMapping createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap,MetaObject metaObject, String columnPrefix) throws SQLException {// 1 ResultMap 中需要 自动映射 的列会缓存起来这是对应的缓存 keyfinal String mapKey resultMap.getId() : columnPrefix;// 2 先从缓存中获取ListUnMappedColumnAutoMapping autoMapping autoMappingsCache.get(mapKey);if (autoMapping null) {autoMapping new ArrayList();// 3 获取未映射的的列名集合也就是数据库返回的列名在 ResultMap 中没有配置例如我们配置的是 resultType 属性就全部没有配置final ListString unmappedColumnNames rsw.getUnmappedColumnNames(resultMap, columnPrefix);for (String columnName : unmappedColumnNames) {String propertyName columnName;/** 4 如果配置了前缀则将列名中的前缀去掉作为属性名*/if (columnPrefix ! null !columnPrefix.isEmpty()) {// When columnPrefix is specified, ignore columns without the prefix.if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {// 如果列名以前缀开头则将前缀去除propertyName columnName.substring(columnPrefix.length());} else {continue;}}/*** 5 根据列名从入参对象中获取对应的属性名称不管大小写都可以找到* {link org.apache.ibatis.reflection.Reflector#caseInsensitivePropertyMap)*/final String property metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());// 6 开始创建 UnMappedColumnAutoMapping 对象if (property ! null metaObject.hasSetter(property)) {if (resultMap.getMappedProperties().contains(property)) {// 如果该属性配置了映射关系则跳过continue;}// 6.1 获取属性名称的 Class 对象final Class? propertyType metaObject.getSetterType(property);if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {final TypeHandler? typeHandler rsw.getTypeHandler(propertyType, columnName);// 6.2.1 创建该属性的 UnMappedColumnAutoMapping 对象设置列名、属性名、类型处理器、是否为原始类型autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));} else {// 6.2.2 执行发现自动映射目标为未知列(或未知属性类型)的行为默认为 NONE不做任何行为configuration.getAutoMappingUnknownColumnBehavior().doAction(mappedStatement, columnName, property, propertyType);}} else {// 执行发现自动映射目标为未知列(或未知属性类型)的行为默认为 NONE不做任何行为configuration.getAutoMappingUnknownColumnBehavior().doAction(mappedStatement, columnName,(property ! null) ? property : propertyName, null);}}autoMappingsCache.put(mapKey, autoMapping);}return autoMapping;
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55. ResultMap 中需要 自动映射 的列会缓存起来这是对应的缓存 key先从autoMappingsCache缓存中获取该 ResultMap 对应的 UnMappedColumnAutoMapping 集合 autoMapping没有的话才进行接下来的解析获取未映射的的列名集合也就是数据库返回的列名在 ResultMap 中没有配置例如我们配置的是 resultType 属性就全部没有配置然后进行遍历如果配置了前缀则将列名中的前缀去掉作为属性名根据列名从入参对象中获取对应的属性名称不管大小写都可以找到开始为该属性创建 UnMappedColumnAutoMapping 对象如果返回对象中有该属性的 setter 方法 获取属性名称的 Class 对象如果有对应的 TypeHandler 类型处理器创建该属性的 UnMappedColumnAutoMapping 对象设置列名、属性名、类型处理器、是否为原始类型添加到autoMapping集合中否则执行发现自动映射目标为未知列(或未知属性类型)的行为默认为 NONE不做任何行为 该属性没有setter方法执行发现自动映射目标为未知列(或未知属性类型)的行为默认为 NONE不做任何行为返回autoMapping并添加到autoMappingsCache缓存中 4.2.4applyPropertyMappings方法 applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)方法将明确被映射的字段设置到返回结果中 登录后复制 private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {// 1 获取 ResultMap 中明确需要进行映射的列名集合final ListString mappedColumnNames rsw.getMappedColumnNames(resultMap, columnPrefix);// 标记是否找到1个以上的属性值延迟加载也算boolean foundValues false;// 2 获取 ResultMap 中所有的 ResultMapping 对象final ListResultMapping propertyMappings resultMap.getPropertyResultMappings();for (ResultMapping propertyMapping : propertyMappings) {// 获取字段名String column prependPrefix(propertyMapping.getColumn(), columnPrefix);if (propertyMapping.getNestedResultMapId() ! null) {// the user added a column attribute to a nested result map, ignore itcolumn null;}/** 3 从结果集获取属性值设置到返回结果中处理以下三种场景* 1. 配置的 column 属性为{prop1:col1,prop2:col2}这种形式* 一般就是嵌套子查询表示将col1和col2的列值设置到嵌套子查询的入参对象的prop1和prop2属性中* 2. 基本类型的属性映射* 3. 多结果集的场景处理该属性来自另一个结果集** 对于没有配置 column 属性不会处理*/if (propertyMapping.isCompositeResult() // 场景1|| (column ! null mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) // 场景2|| propertyMapping.getResultSet() ! null) { // 场景3// 4 完成映射从结果集中获取到对应的属性值Object value getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);// issue #541 make property optionalfinal String property propertyMapping.getProperty();if (property null) {// 4.1 没有配置对应的 Java 属性则跳过continue;} else if (value DEFERRED) {// 4.2 如果是占位符则跳过foundValues true;continue;}if (value ! null) {foundValues true;}if (value ! null || (configuration.isCallSettersOnNulls() !metaObject.getSetterType(property).isPrimitive())) {// gcode issue #377, call setter on nulls (value is not found)// 4.3 将属性值设置到返回结果中metaObject.setValue(property, value); // 设置属性值}}}return foundValues;
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51. 获取 ResultMap 中明确需要进行映射的列名集合mappedColumnNames获取 ResultMap 中所有的 ResultMapping 对象然后进行遍历从结果集获取属性值设置到返回结果中需要满足下面三个条件中的一个 配置的 column 属性为{prop1:col1,prop2:col2}这种形式一般就是嵌套子查询表示将col1和col2的列值设置到嵌套子查询的入参对象的prop1和prop2属性中基本类型的属性映射多结果集的场景处理该属性来自另一个结果集存储过程相关本文暂不分析 完成映射调用getPropertyMappingValue方法从结果集中获取到对应的属性值value 没有配置对应的 Java 属性则跳过如果是DEFERRED占位符(延迟加载)则跳过将属性值设置到返回结果中 我们来看看getPropertyMappingValue方法 4.2.4.1getPropertyMappingValue方法 4.2.4.1getPropertyMappingValue方法 getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)方法 完成映射从结果集中获取到对应的属性值 登录后复制 private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {if (propertyMapping.getNestedQueryId() ! null) { // 嵌套子查询// 1 执行嵌套子查询返回查询结果如果需要延迟记载则返回的是 DEFERREDreturn getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);} else if (propertyMapping.getResultSet() ! null) { // 多结果集存储过程相关暂时忽略// 2 多结果集处理延迟加载返回占位符addPendingChildRelation(rs, metaResultObject, propertyMapping);return DEFERRED;} else { // 结果映射// 获取 TypeHandler 类型处理器final TypeHandler? typeHandler propertyMapping.getTypeHandler();final String column prependPrefix(propertyMapping.getColumn(), columnPrefix);// 3 通过 TypeHandler 类型处理器从结果集中获取该列对应的属性值return typeHandler.getResult(rs, column);}
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17. 可以看到该ResultMapping属性配置可能有三种情况 如果是嵌套子查询则调用getNestedQueryMappingValue方法执行嵌套子查询返回查询结果如果需要延迟记载则返回的是 DEFERRED如果是多结果集存储过程相关则调用addPendingChildRelation方法 多结果集处理延迟加载返回占位符本文暂不分析正常情况获取 TypeHandler 类型处理器通过它从结果集中获取该列对应的属性值 我们这里继续看到第1步中的调用的方法 4.2.4.2getNestedQueryMappingValue方法 4.2.4.2getNestedQueryMappingValue方法 getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)方法 执行嵌套子查询返回查询结果如果需要延迟记载则返回的是 DEFERRED 登录后复制 private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {// 1 获取嵌套子查询关联的IDfinal String nestedQueryId propertyMapping.getNestedQueryId();// 获得属性名final String property propertyMapping.getProperty();// 获得嵌套子查询的 MappedStatement 对象final MappedStatement nestedQuery configuration.getMappedStatement(nestedQueryId);// 获得嵌套子查询的参数类型final Class? nestedQueryParameterType nestedQuery.getParameterMap().getType();// 2 准备好嵌套子查询的入参final Object nestedQueryParameterObject prepareParameterForNestedQuery(rs, propertyMapping,nestedQueryParameterType, columnPrefix);Object value null;if (nestedQueryParameterObject ! null) {// 3 获得嵌套子查询的 BoundSql 对象final BoundSql nestedBoundSql nestedQuery.getBoundSql(nestedQueryParameterObject);// 4 获得嵌套子查询本次查询的 CacheKey 对象final CacheKey key executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);// 嵌套子查询的返回 Java Typefinal Class? targetType propertyMapping.getJavaType();// 5 检查缓存中已存在if (executor.isCached(nestedQuery, key)) {// 5.1 创建 DeferredLoad 对象并通过该 DeferredLoad 对象从缓存中加载结果对象executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);// 5.2 返回已定义value DEFERRED;} else { // 6 缓存中不存在// 6.1 创建 ResultLoader 对象final ResultLoader resultLoader new ResultLoader(configuration, executor, nestedQuery,nestedQueryParameterObject, targetType, key, nestedBoundSql);if (propertyMapping.isLazy()) { // 6.2 如果要求延迟加载则延迟加载// 6.2.1 如果该属性配置了延迟加载则将其添加到 ResultLoader.loaderMap 中等待真正使用时再执行嵌套查询并得到结果对象lazyLoader.addLoader(property, metaResultObject, resultLoader);// 6.2.2 返回延迟加载占位符value DEFERRED;} else { // 6.3 如果不要求延迟加载则直接执行加载对应的值value resultLoader.loadResult();}}}return value;
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43. 获取嵌套子查询关联的ID、属性名、嵌套子查询的 MappedStatement 对象、嵌套子查询的参数类型从结果集中获取参数值准备好嵌套子查询的入参调用prepareParameterForNestedQuery方法获得嵌套子查询的 BoundSql 对象获得嵌套子查询本次查询的 CacheKey 对象、嵌套子查询的返回 Java Type检查缓存中已存在本次子查询的数据已存在的话则进行下面两步 则创建 DeferredLoad 对象并通过该 DeferredLoad 对象从缓存中加载结果对象这也算延迟加载嵌套子查询的结果在缓存中然后会在查询接口后进行加载可以回到 《SQL执行过程(一)之Executor》的BaseExecutor小节中的query方法的第6步看看返回DEFERRED延迟加载默认对象 缓存中不存在本次子查询的数据 创建 ResultLoader 对象resultLoader如何该属性还要求了是延迟加载 则将其添加到ResultLoader.loaderMap 中等待真正使用时再执行嵌套查询并得到结果对象可以回到4.2.1createResultObject方法的第4步看一下如果存在嵌套子查询并且要求延迟加载那么为该返回结果的实例对象创建一个动态代理对象(Javassist)后续将需要延迟加载的属性放入 lazyLoader (就是上面的ResultLoader)中即可返回DEFERRED延迟加载默认对象 否在直接加载resultLoader对象获取到该属性值 最后返回该属性值或者DEFERRED延迟加载默认对象 这里我们再来看到第2步中调用的方法 4.2.4.2.1prepareParameterForNestedQuery方法 4.2.4.2.1prepareParameterForNestedQuery方法 prepareParameterForNestedQuery(ResultSet rs, ResultMapping resultMapping, Class? parameterType, String columnPrefix)方法为嵌套子查询准备好入参 登录后复制 private Object prepareParameterForNestedQuery(ResultSet rs, ResultMapping resultMapping, Class? parameterType,String columnPrefix) throws SQLException {if (resultMapping.isCompositeResult()) { // 嵌套子查询是否有多个属性映射// 从结果集中获取多个属性值设置到入参对象中return prepareCompositeKeyParameter(rs, resultMapping, parameterType, columnPrefix);} else {// 从结果集中直接获取嵌套查询的入参return prepareSimpleKeyParameter(rs, resultMapping, parameterType, columnPrefix);}
} 1.2.3.4.5.6.7.8.9.10. 嵌套子查询是有多个属性映射则调用prepareCompositeKeyParameter方法从结果集中获取多个属性值设置到入参对象中配置的column属性为{prop1:col1,prop2:col2}这种形式表示将col1和col2的列值设置到嵌套子查询的入参对象的prop1和prop2属性中只有一个属性映射则调用prepareSimpleKeyParameter方法从结果集中直接获取嵌套查询的入参 来看看第1、2步调用的方法 4.2.4.2.2prepareCompositeKeyParameter方法4.2.4.2.3prepareSimpleKeyParameter方法 4.2.4.2.2prepareCompositeKeyParameter方法 prepareCompositeKeyParameter(ResultSet rs, ResultMapping resultMapping, Class? parameterType, String columnPrefix)方法 处理嵌套子查询有多个属性映射作为入参的场景获取到多个属性值到子查询的入参中 登录后复制 private Object prepareCompositeKeyParameter(ResultSet rs, ResultMapping resultMapping, Class? parameterType,String columnPrefix) throws SQLException {// 创建一个嵌套子查询的入参的实例对象final Object parameterObject instantiateParameterObject(parameterType);final MetaObject metaObject configuration.newMetaObject(parameterObject);// 标记是否找到一个或以上的属性值boolean foundValues false;for (ResultMapping innerResultMapping : resultMapping.getComposites()) {// 获取嵌套子查询的入参该属性的 Java Typefinal Class? propType metaObject.getSetterType(innerResultMapping.getProperty());final TypeHandler? typeHandler typeHandlerRegistry.getTypeHandler(propType);//通过 TypeHandler 根据该属性的 column 列名从该结果集中获取值final Object propValue typeHandler.getResult(rs, prependPrefix(innerResultMapping.getColumn(), columnPrefix));// issue #353 #560 do not execute nested query if key is nullif (propValue ! null) {// 设置属性值到入参对象中metaObject.setValue(innerResultMapping.getProperty(), propValue);foundValues true;}}return foundValues ? parameterObject : null;
} 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22. 创建一个嵌套子查询的入参的实例对象parameterObject调用instantiateParameterObject方法很简单点击去看一下就知道了开始遍历ResultMapping中的ListResultMapping composites组合字段获取嵌套子查询的入参该属性对应的 TypeHandler 处理器通过 TypeHandler 根据该属性的 column 列名从该结果集中获取值设置属性值到子查询的入参对象中返回parameterObject子查询入参对象 4.2.4.2.3prepareSimpleKeyParameter方法 prepareSimpleKeyParameter(ResultSet rs, ResultMapping resultMapping, Class? parameterType, String columnPrefix)方法从结果集中直接获取嵌套查询的入参 登录后复制 private Object prepareSimpleKeyParameter(ResultSet rs, ResultMapping resultMapping, Class? parameterType,String columnPrefix) throws SQLException {final TypeHandler? typeHandler;if (typeHandlerRegistry.hasTypeHandler(parameterType)) {typeHandler typeHandlerRegistry.getTypeHandler(parameterType);} else {typeHandler typeHandlerRegistry.getUnknownTypeHandler();}// 根据类型处理器从结果集中获取该列的值作为嵌套查询的入参return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));
} 1.2.3.4.5.6.7.8.9.10.11. 根据Java Type获取到 TypeHandler 类型处理器根据TypeHandler从结果集中将该列对应的值转化成入参 4.3storeObject方法 storeObject(ResultHandler? resultHandler, DefaultResultContextObject resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs)方法 将返回结果 对象保存至 resultHandler或者设置到父对象 parentMapping(存储过程相关本文暂不分析)的对应属性中 登录后复制 private void storeObject(ResultHandler? resultHandler, DefaultResultContextObject resultContext,Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {if (parentMapping ! null) {// 嵌套查询或嵌套映射将返回结果设置到父对象的对应属性中linkToParents(rs, parentMapping, rowValue);} else {// 普通映射将结果对象保存到 ResultHandler 中callResultHandler(resultHandler, resultContext, rowValue);}
} 1.2.3.4.5.6.7.8.9.10. 如果parentMapping不为空则调用linkToParents方法嵌套查询或嵌套映射将返回结果设置到父对象的对应属性中存储过程相关本文暂不分析调用callResultHandler方法普通映射将结果对象保存到 ResultHandler 中 来看到第2步调用的方法 4.3.1callResultHandler方法 callResultHandler(ResultHandler? resultHandler, DefaultResultContextObject resultContext, Object rowValue)方法 普通映射将结果对象保存到 ResultHandler 中 登录后复制 private void callResultHandler(ResultHandler? resultHandler, DefaultResultContextObject resultContext, Object rowValue) {/** 递增返回结果数量 resultCount* 保存返回结果 resultObject*/resultContext.nextResultObject(rowValue);// 将返回结果保存至 ResultHandler 中((ResultHandlerObject) resultHandler).handleResult(resultContext);
} 1.2.3.4.5.6.7.8.9. 在resultContext上下文对象对象中暂存返回结果rowValue并递增返回结果的数量可以回到4.handleRowValuesForSimpleResultMap方法看一下通过resultHandler对resultContext上下文对象暂存的返回结果进行处理在DefaultResultHandler中你可以看到 登录后复制 public class DefaultResultHandler implements ResultHandlerObject {private final ListObject list;public DefaultResultHandler() {list new ArrayList();}Overridepublic void handleResult(ResultContext? context) {list.add(context.getResultObject());}
} 1.2.3.4.5.6.7.8.9.10.11.12.13. 就是往List集合中添加返回结果 结束语 回顾到3.handleRowValues方法中上面已经对结果集(不含嵌套ResultMap)进行映射的整个过程进行了分析在defaultResultHandler和multipleResults都可以获取到映射后的结果 本来想继续分析结果集(含嵌套ResultMap)这种情况的调用的是handleRowValuesForNestedResultMap方法由于上面已经嵌套太多层方法了就不再分析第二种更复杂的情况本文本身就不好编排再进行嵌套就无法阅读了可以参考 DefaultResultSetHandler.java根据注释进行理解 其中涉及到的DefaultResultContext和DefaultResultHandler都比较简单也不列出来了自行根据注释查看一下 总结 本文分析了DefaultResultSetHandler是如何处理结果集进行映射转换成Java对象的总的来说就是根据ResultMap对象对于不同的场景进行处理分析映射成我们需要的Java对象需要考虑的情况太多所以这个类比较复杂这里也仅对结果集(不含嵌套ResultMap)进行映射的整个过程进行了分析关于含嵌套ResultMap的结果集来说可能更加稍微复杂一点不过也相差无几可以参考 DefaultResultSetHandler.java 到这里SQL执行过程算是结束了但是其中还有一部分延迟加载的内容没有分析本来准备在这篇文档中分析的发现已经写太多内容了所以放在下一篇文档中