*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包

一个能让你学习技术和赚钱方法的公众号,持续更新
*************************************优雅的分割线 **********************************
前言

在前面的文章中,已经介绍了三种SqlSource的代码,下面开始介绍执行select语句对查询结果集封装的过程。
ResultSetHandler

前面的文章中得知,mybatis会将结果集按照映射配置文件中定义的映射规则,如resultMap节点,映射成相应的结果对象。

在StatementHandler接口执行完指定的select语句后,会将查询结果集交给ResultSetHandler完成映射处理。

ResultSetHandler接口代码如下:

/**

  • 处理select查询的结果集

  • @author Clinton Begin
    */
    public interface ResultSetHandler {

    /**

    • 处理结果集,生成结果集集合
    • @param stmt
    • @param
    • @return
    • @throws SQLException
      */
      List handleResultSets(Statement stmt) throws SQLException;

    /**

    • 处理结果集,返回相应的游标
    • @param stmt
    • @param
    • @return
    • @throws SQLException
      */
      Cursor handleCursorResultSets(Statement stmt) throws SQLException;

    /**

    • 处理存储过程
    • @param cs
    • @throws SQLException
      */
      void handleOutputParameters(CallableStatement cs) throws SQLException;

}

ResultSetHandler只有DefaultResultSetHandler一个实现,该类是处理结果集映射的核心类。核心字段如下所示:

public class DefaultResultSetHandler implements ResultSetHandler {

private static final Object DEFERRED = new Object();/*** MyBatis执行器*/
private final Executor executor;
private final Configuration configuration;
/*** Sql节点*/
private final MappedStatement mappedStatement;
/*** 游标*/
private final RowBounds rowBounds;
/*** 参数处理器*/
private final ParameterHandler parameterHandler;
/*** 结果集处理器*/
private final ResultHandler<?> resultHandler;
/*** Sql对象*/
private final BoundSql boundSql;
private final TypeHandlerRegistry typeHandlerRegistry;
/*** 对象工厂和反射工厂*/
private final ObjectFactory objectFactory;
private final ReflectorFactory reflectorFactory;/*** 映射缓存*/
private final Map<CacheKey, Object> nestedResultObjects = new HashMap<>();
private final Map<String, Object> ancestorObjects = new HashMap<>();/*** 自动映射列缓存*/
private final Map<String, List<UnMappedColumnAutoMapping>> autoMappingsCache = new HashMap<>();/*** 记录是否使用构造器创建映射对象*/
private boolean useConstructorMappings;

}

handlerResultSets方法

通过select语句查询司机卡得到的结果集由handlerResultSets方法进行处理。该方法可以处理由Statement、PreparedStatement、CallableStatement产生的结果集。其中,Statement用于处理静态SQL,PrepareStatement用于处理预处理的SQL,CallableStatement用于处理存储过程,存储过程的结果集可能有多个,mybatis中对多结果集也进行了处理。由于java开发多数是mysql,而mysql中存储过程使用频率非常之少,因此这里不对多结果集进行讲解。

handleResultSets方法的代码如下。

/**
* ☆
* select查询到的结果集会在这里被处理
*
* @param stmt
* @return
* @throws SQLException
*/
@Override
public List handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity(“handling results”).object(mappedStatement.getId());

    // 保存映射得到的结果集对象final List<Object> multipleResults = new ArrayList<>();// 结果集数量int resultSetCount = 0;// 获取第一个结果集ResultSetWrapper rsw = getFirstResultSet(stmt);// 获取到sql节点所有的resultMap(一般只有一个)List<ResultMap> resultMaps = mappedStatement.getResultMaps();// resultMap的数量int resultMapCount = resultMaps.size();validateResultMapsCount(rsw, resultMapCount);// 遍历resultMapswhile (rsw != null && resultMapCount > resultSetCount) {// 获取指定下标的resultMapResultMap resultMap = resultMaps.get(resultSetCount);// 处理resultSethandleResultSet(rsw, resultMap, multipleResults, null);// 获取下一个结果集rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;}// 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);
}

查询到的结果集可能有多个,mybatis默认先处理单结果集,getFirstResultSet方法用于获取第一个结果集对象。而getNextResultSet则是用于获取下一个结果集

/*** 获取第一个结果集对象。* 在操作存储过程时,可能会得到多个结果集* 该方法只获取第一个结果集** @param stmt* @return* @throws SQLException*/
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {// 获取结果集ResultSet rs = stmt.getResultSet();// 结果集如果为null就继续向下获取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 resultsetbreak;}}}return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}/*** 获取下一个resultSet结果集** @param stmt* @return*/
private ResultSetWrapper getNextResultSet(Statement stmt) {// Making this method tolerant of bad JDBC driverstry {// 检测jdbc是否支持多结果集if (stmt.getConnection().getMetaData().supportsMultipleResultSets()) {// 检测是否还存在需要处理的结果集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;
}

在上面的代码中,DefaultResultHandler在获取到结果集对象之后,会将其封装成ResultSetWrapper对象再进行处理。ResultSetWrapper对象中记录了结果集的一些元数据,并提供了一系列操作ResultSet的辅助方法,下面是ResultSetWrapper的核心字段。

/**

  • 对ResultSet进行封装

  • 存放了ResultSet的元数据

  • @author Iwao AVE!
    */
    public class ResultSetWrapper {

    /**

    • 查询得到的结果集
      /
      private final ResultSet resultSet;
      /
      *
    • 一堆类型处理器
      /
      private final TypeHandlerRegistry typeHandlerRegistry;
      /
      *
    • resultSet每列列名
      /
      private final List columnNames = new ArrayList<>();
      /
      *
    • 每列对应的java类型
      /
      private final List classNames = new ArrayList<>();
      /
      *
    • 每列对应的jdbc类型
      /
      private final List jdbcTypes = new ArrayList<>();
      /
      *
    • key是列名,value是TypeHandler
      /
      private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
      /
      *
    • 记录被映射的列名
      /
      private final Map<String, List> mappedColumnNamesMap = new HashMap<>();
      /
      *
    • 记录未映射的列名
      */
      private final Map<String, List> unMappedColumnNamesMap = new HashMap<>();
      }

在ResultSetWrapper的构造方法中,会初始化columnNames、jdbcTypes、classNames三个集合,代码如下。

public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {super();this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();this.resultSet = rs;// 获取resultSet的元信息final ResultSetMetaData metaData = rs.getMetaData();// 获取resultSet列数final int columnCount = metaData.getColumnCount();// 遍历每一列,封装 列名、jdbc类型、java类型for (int i = 1; i <= columnCount; i++) {columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));classNames.add(metaData.getColumnClassName(i));}
}

简单映射

介绍完整体的流程,下面来看handleResultSet方法。该方法的核心功能是完成对单个结果集的映射(即单表查询的映射)。代码如下。

/*** 根据resultMap定义的映射规则去处理resultSet。并将映射的结果添加到multipleResults集合** @param rsw* @param resultMap* @param multipleResults* @param parentMapping* @throws SQLException*/
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {try {if (parentMapping != null) {// 处理结果集中的嵌套映射。(resultMap中套着resultMap)handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);} else {if (resultHandler == null) {// 用户没有指定resultHandler,就用DefaultResultHandlerDefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);// 对resultSet进行映射,并将映射结果添加到defaultResultHandlerhandleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);// 将defaultResultHandler中保存的集合添加到multipleResultsmultipleResults.add(defaultResultHandler.getResultList());} else {// 使用用户指定的resultHandler处理结果集handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);}}} finally {// issue #228 (close resultsets)closeResultSet(rsw.getResultSet());}
}

该方法的核心代码就是handleRowValues。方法中判断是否包含嵌套映射去决定处理简单映射还是嵌套映射,代码如下。

/*** 结果集映射核心方法** @param rsw* @param resultMap* @param resultHandler* @param rowBounds* @param parentMapping* @throws SQLException*/
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {if (resultMap.hasNestedResultMaps()) {ensureNoRowBounds();checkResultHandler();handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);} else {// 简单结果集映射(单表)handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);}
}

下面先看简单映射。简单映射的核心代码在handleRowValuesForSimpleResultMap方法中,下面先分析该方法执行流程。

调用skipRows方法,根据RowBounds中的offset值定位到指定的记录行。RowBounds叫做游标,后面的文章会对其进行介绍。
调用shouldProcessMoreRows方法检测是否还有需要映射的记录。
通过resolveDiscriminatedResultMap方法确定映射要使用的ResultMap对象
调用getRowValue方法对Result中的一行记录进行映射。通过createResultObject方法创建映射后的结果对象。通过shouldApplyAutomaticMap平时方法检测是否开启了自动映射功能通过applyAutomaiticMappings方法自动映射ResultMap中为明确映射的列通过applyPropertyMap平时方法映射ResultMap中明确映射的列,到这里该行记录的数据已经完全映射到了结果对象的相应属性中。
调用storeObject方法保存映射得到的结果集对象

handleRowValuesForSimpleResultMap代码如下。

/*** 简单结果集映射处理** @param rsw* @param resultMap* @param resultHandler* @param rowBounds* @param parentMapping* @throws SQLException*/
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)throws SQLException {DefaultResultContext<Object> resultContext = new DefaultResultContext<>();ResultSet resultSet = rsw.getResultSet();// 通过游标的offset值定位到指定的记录行skipRows(resultSet, rowBounds);// 检测是否还有需要映射的记录while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {// 确定使用的ResultMap对象。多数情况下,这里指的还是传入的ResultMapResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);// 映射Object rowValue = getRowValue(rsw, discriminatedResultMap, null);// 保存映射结果storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);}
}

skipRows方法是根据RowBounds.offset字段的值定位到指定的记录。

/*** 通过游标定位到指定行** @param rs* @param rowBounds* @throws SQLException*/
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;}}}
}

定位到指定的记录行之后,再通过shouldProcessMoreRows方法检测是否还有需要映射的行。

/*** 检测是否还有需要映射的数据** @param context* @param rowBounds* @return*/
private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) {return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
}

resolveDiscriminatedResultMap方法会根据ResultMap对象中记录的Discriminator以及参与映射的列值,选择映射操作最终使用的ResultMap,具体实现如下,。

/*** 根据ResultMap中记录的Discriminator对象以及参与映射的记录行中的列值* 确定使用的ResultMap对象** @param rs* @param resultMap* @param columnPrefix* @return* @throws SQLException*/
public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {Set<String> pastDiscriminators = new HashSet<>();// 通过discriminator标签去确定使用哪个ResultMap。使用不多,不进行注释Discriminator discriminator = resultMap.getDiscriminator();while (discriminator != null) {final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);final String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));if (configuration.hasResultMap(discriminatedMapId)) {resultMap = configuration.getResultMap(discriminatedMapId);Discriminator lastDiscriminator = discriminator;discriminator = resultMap.getDiscriminator();if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {break;}} else {break;}}return resultMap;
}
private Object getDiscriminatorValue(ResultSet rs, Discriminator discriminator, String columnPrefix) throws SQLException {final ResultMapping resultMapping = discriminator.getResultMapping();final TypeHandler<?> typeHandler = resultMapping.getTypeHandler();return typeHandler.getResult(rs, prependPrefix(resultMapping.getColumn(), columnPrefix));
}

通过上面方法的处理确定了映射使用的ResultMap对象,之后会调用getRowValue完成对该记录的映射。首先根据ResultMap指定的类型创建对应的结果对象和MetaObject,再根据配置信息决定是否自动映射ResultMap中未明确映射的列,映射完毕后返回结果对象。代码如下。

/*** 映射** @param rsw* @param resultMap* @param columnPrefix* @return* @throws SQLException*/
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {// 与延迟加载有关final ResultLoaderMap lazyLoader = new ResultLoaderMap();// 创建该行记录映射之后的结果对象,就是resultMap的type属性指定的类Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {// 创建上面对象的metaObjectfinal MetaObject metaObject = configuration.newMetaObject(rowValue);// 成功映射任意属性,则为true,否则为falseboolean foundValues = this.useConstructorMappings;// 检测是否需要自动映射if (shouldApplyAutomaticMappings(resultMap, false)) {// 自动映射resultMap未指定的列foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;}// 映射resultMap指定的列foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;foundValues = lazyLoader.size() > 0 || foundValues;// 如果没有映射任何属性,就根据mybatis-config.xml配置的returnInstanceForEmptyRow配置决定如何返回rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;}return rowValue;
}

该方法中,createResultObject方法负责创建数据库记录映射得到的结果对象,该方法会返回结果集的列数、constructorResultMappings集合等信息,选择不同的方式创建结果对象。具体实现如下。

/*** 创建该行记录映射之后的结果对象,就是resultMap的type属性指定的类** @param rsw* @param resultMap* @param lazyLoader* @param columnPrefix* @return* @throws SQLException*/
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {this.useConstructorMappings = false; // reset previous mapping result// 构造的参数类型final List<Class<?>> constructorArgTypes = new ArrayList<>();// 构造参数final List<Object> constructorArgs = new ArrayList<>();// 创建该行记录的结果对象。该方法是该步骤的核心Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);// TODO 如果包含嵌套查询且配置了延迟加载,就创建代理对象if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {final List<ResultMapping> 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;}}}// 记录是否使用构造器创建对象this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping resultreturn resultObject;
}/*** 创建映射结果对象** @param rsw* @param resultMap* @param constructorArgTypes* @param constructorArgs* @param columnPrefix* @return* @throws SQLException*/
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)throws SQLException {// 根据resultMap配置的type属性去创建对应的MetaClassfinal Class<?> resultType = resultMap.getType();final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);// 获取到constructor节点final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();if (hasTypeHandlerForResultObject(rsw, resultType)) {// 结果集只有一列,并且存在TypeHandler对象可以将该列转换成resultType指定的值(Integer、String等)return createPrimitiveResultObject(rsw, resultMap, columnPrefix);} else if (!constructorMappings.isEmpty()) {// resultMap中指定了constructor标签,通过反射方式调用构造方法创建对象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);
}

完成了对映射结果对象的创建后,下面就会将一行记录的各个列映射到该结果集对象的对应属性中。在成功创建对象并且获取到MetaObject之后,会调用shouldApplyAutomaticMappings方法检测是否允许自动映射,如果允许则调用applyAutomaiticMappings方法对ResultMap未指定的列进行自动映射。

/*** 是否需要自动映射。** @param resultMap* @param isNested* @return*/
private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {if (resultMap.getAutoMapping() != null) {return resultMap.getAutoMapping();} else {if (isNested) {return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior();} else {return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();}}
}/*** 自动映射未指定的列* @param rsw* @param resultMap* @param metaObject* @param columnPrefix* @return* @throws SQLException*/
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {// 查找需要自动映射的列List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);boolean foundValues = false;if (!autoMapping.isEmpty()) {// 映射列不为空,一一映射for (UnMappedColumnAutoMapping mapping : autoMapping) {// 从resultSet获取值final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);if (value != null) {foundValues = true;}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;
}

createAutomaticMappings方法负责为未映射的列查找对应的属性,并将二者关联起来封装成UnMappedColumnAutoMapping对象。createAutomaticMappings方法的具体实现如下。

/*** 查找需要自动映射的列* @param rsw* @param resultMap* @param metaObject* @param columnPrefix* @return* @throws SQLException*/
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {final String mapKey = resultMap.getId() + ":" + columnPrefix;// 先从缓存中找List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);if (autoMapping == null) {autoMapping = new ArrayList<>();// 获取未映射的列final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);for (String columnName : unmappedColumnNames) {// 默认列名就是属性名String propertyName = columnName;// 列前缀不为空时处理。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;}}// 根据列名查找对应的属性final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());// 属性不为空并且有set方法if (property != null && metaObject.hasSetter(property)) {// 该列已经映射,不重复映射if (resultMap.getMappedProperties().contains(property)) {continue;}final Class<?> propertyType = metaObject.getSetterType(property);if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {// 查找对应的TypeHandler对象final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);// 将该列添加到autoMapping集合中autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));} else {configuration.getAutoMappingUnknownColumnBehavior().doAction(mappedStatement, columnName, property, propertyType);}} else {configuration.getAutoMappingUnknownColumnBehavior().doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);}}// 存放到缓存autoMappingsCache.put(mapKey, autoMapping);}return autoMapping;
}

通过applyAutomaiticMappings方法处理完自动映射之后,后续会通过applyPropertyMappings方法对ResultMap中指定的列进行映射,核心代码如下。

/*** 根据配置去映射* @param rsw* @param resultMap* @param metaObject* @param lazyLoader* @param columnPrefix* @return* @throws SQLException*/
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)throws SQLException {// 获取需要映射的列名final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);boolean foundValues = false;// 获取所有resultMappingfinal List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();for (ResultMapping propertyMapping : propertyMappings) {// 获取列名String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);if (propertyMapping.getNestedResultMapId() != null) {// 判断该节点是否是对其他ResultMapping进行引用column = null;}// 场景1:column是{prop1=col1,prop2=col2}形式if (propertyMapping.isCompositeResult()// 场景2:基本类型的属性映射|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))// 场景3:多结果集处理|| propertyMapping.getResultSet() != null) {// 完成映射,得到属性值Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);// 获取属性名称final String property = propertyMapping.getProperty();if (property == null) {continue;} else if (value == DEFERRED) {// DEFERRED指占位符对象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')// 设置属性值metaObject.setValue(property, value);}}}return foundValues;
}

其中,映射操作是在getPropertyMappingValue方法中完成,具体代码如下,

/*** 完成映射操作并获取属性值* @param rs* @param metaResultObject* @param propertyMapping* @param lazyLoader* @param columnPrefix* @return* @throws SQLException*/
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)throws SQLException {if (propertyMapping.getNestedQueryId() != null) {// 存在嵌套查询return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);} else if (propertyMapping.getResultSet() != null) {// 多结果集的处理addPendingChildRelation(rs, metaResultObject, propertyMapping);return DEFERRED;} else {final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);// 使用typeHandler来获取属性值return typeHandler.getResult(rs, column);}
}

到这里,已经得到了一个完整映射的结果对象,之后DefaultResultSetHandler会通过storeObject方法将该结果对象保存到合适的位置,这样该行记录就完成了。如果是嵌套映射或者嵌套查询的结果对象则保存到父对象对应的属性中,如果是简单映射则保存到ResultHandler中。

/*** 保存映射结果* @param resultHandler* @param resultContext* @param rowValue* @param parentMapping* @param rs* @throws SQLException*/
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {if (parentMapping != null) {// 嵌套映射,保存在父对象属性中linkToParents(rs, parentMapping, rowValue);} else {// 普通映射,保存在ResultHandlercallResultHandler(resultHandler, resultContext, rowValue);}
}private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {resultContext.nextResultObject(rowValue);((ResultHandler<Object>) resultHandler).handleResult(resultContext);
}

至此,简单映射的流程就介绍完了。
*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包

一个能让你学习技术和赚钱方法的公众号,持续更新
*************************************优雅的分割线 **********************************

Mybatis源码阅读(三):结果集映射3.1 —— ResultSetBuilder与简单映射相关推荐

  1. mybatis源码阅读(三):mybatis初始化(下)mapper解析

    转载自 mybatis源码阅读(三):mybatis初始化(下)mapper解析 MyBatis 的真正强大在于它的映射语句,也是它的魔力所在.由于它的异常强大,映射器的 XML 文件就显得相对简单. ...

  2. mybatis源码阅读(八) ---Interceptor了解一下

    转载自  mybatis源码阅读(八) ---Interceptor了解一下 1 Intercetor MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用.默认情况下,MyBatis允许 ...

  3. mybatis源码阅读(五) ---执行器Executor

    转载自  mybatis源码阅读(五) ---执行器Executor 1. Executor接口设计与类结构图 public interface Executor {ResultHandler NO_ ...

  4. mybatis源码阅读(二):mybatis初始化上

    转载自  mybatis源码阅读(二):mybatis初始化上 1.初始化入口 //Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与 ...

  5. mybatis源码阅读(一):SqlSession和SqlSessionFactory

    转载自  mybatis源码阅读(一):SqlSession和SqlSessionFactory 一.接口定义 听名字就知道这里使用了工厂方法模式,SqlSessionFactory负责创建SqlSe ...

  6. Mybatis源码阅读(一):Mybatis初始化1.1 解析properties、settings

    *************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如 ...

  7. Mybatis源码阅读之二——模板方法模式与Executor

    [系列目录] Mybatis源码阅读之一--工厂模式与SqlSessionFactory 文章目录 一. 模板方法模式 二. 同步回调与匿名函数 三. Executor BaseExecutor与其子 ...

  8. Mybatis 源码阅读环境搭建

    Mybatis源码阅读环境搭建 前言 一.下载mybatis的源码 二.编译源码 三.创建测试项目 前言     mybatis源码阅读环境搭建还是比较简单的,接下来我们讲解一下如何搭建该源码阅读环境 ...

  9. 24 UsageEnvironment使用环境抽象基类——Live555源码阅读(三)UsageEnvironment

    24 UsageEnvironment使用环境抽象基类--Live555源码阅读(三)UsageEnvironment 24 UsageEnvironment使用环境抽象基类--Live555源码阅读 ...

  10. mybatis源码阅读(七) ---ResultSetHandler了解一下

    转载自  mybatis源码阅读(七) ---ResultSetHandler了解一下 1.MetaObject MetaObject用于反射创建对象.反射从对象中获取属性值.反射给对象设置属性值,参 ...

最新文章

  1. 为了智能驾驶,李彦宏要改造城市道路
  2. Servlet与JSP学习笔记(五) JSP核心(下)
  3. VBS递归遍历文件夹
  4. Laravel - Artisan 个人常用总结
  5. 2.1 CPU 上下文切换(上)
  6. P5825-排列计数【EGF,NTT】
  7. (四)将容器部署到Azure上的Kubernetes
  8. 【李宏毅2020 ML/DL】P73 More about Anomaly Detection
  9. tensorflow之视频质量诊断
  10. galerkin有限元法matlab实现,应用Matlab实现有限元分析.pdf
  11. Python 情人节超强技能 导出微信聊天记录生成词云
  12. 软件测试方法口诀,自测记忆法
  13. 解决Ubuntu无法调节外接显示器亮度的问题
  14. 5.flask与数据库
  15. Python 离散小波变换(DWT) pywt库
  16. 万字HTTP学习笔记
  17. SAP中物料单位更改处理案例
  18. Android音乐播放器制作(一)扫描本地音乐显示在手机上
  19. Learning to Memorize Entailment and Discourse Relations for Persona-Consistent Dialogues论文学习
  20. 深度:养老康复器械龙头即将上市,美的、新松进军养老康复机器人,老龄化加速千亿康复市场到来!

热门文章

  1. linux nmap下载教程,Linux_在Linux系统上用nmap扫描SSL漏洞的方法,以下载nmap 6.45及以上版本。如 - phpStudy...
  2. java8 stream index_Java8的stream用法整理
  3. linux php curl.so,linux中php如何安装CURL扩展方法
  4. java web 来源页_Java:Java Web--分页效果
  5. 2018python做图形界面哪个库简单_2018年常见的python编程开发库都有哪些类型
  6. java替换图片中文字_Java 添加、替换、删除Word中的图片
  7. java默认数组值_数组元素默认的初始值都是什么
  8. 高斯拟合 vc++代码_NMA2020W1 极大似然法模型拟合与bootstrap
  9. 优先队列默认是小顶堆吗_一分钟带你读懂什么是堆?
  10. 实现定时中断_无线传感器网络实验报告(二)Timer定时应用实验