MyBatis源码解析--加载流程(9)

MyBatis ResultSetHandler 结果集的解析过程 该篇讲述了mybatis在执行完数据库后进行结果集的大致解析过程。ResultSetWrapper是对JDBC中的ResultSet对象的封装结果集解析的重点在这个方法中XML中的resultMap可以定义多个映射关系。如果多个结果集对应一个映射关系就需要第四部分(对resultSets标签的处理) 阅读详情

ResultSetHandler 会将查询结果的ResultSet转换成映射的对应结果
ResultSetWrapper

public class ResultSetWrapper {
//ResultSet对象	
  private final ResultSet resultSet;
  private final TypeHandlerRegistry typeHandlerRegistry;
  //字段名称的数组
  private final List<String> columnNames = new ArrayList<>();
  //字段的JAVA Type数组
  private final List<String> classNames = new ArrayList<>();
  //字段的JDBC Type数组
  private final List<JdbcType> jdbcTypes = new ArrayList<>();
  //记录每列对应的TypeHandler对象,key是列名,value是TypeHandler
  private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
  //记录被映射的列名,key是ResultMap的id,v是映射的列名集合
  private final Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
  //记录了未映射的列名,key是ResultMap的id,v是未映射的列名集合
  private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();

  public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
    super();
    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.resultSet = rs;
    final ResultSetMetaData metaData = rs.getMetaData();
    final int columnCount = metaData.getColumnCount();
    //遍历 ResultSetMetaData 的字段们,解析出 columnNames、jdbcTypes、classNames 属性
    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));
    }
  }

  public ResultSet getResultSet() {
    return resultSet;
  }

  public List<String> getColumnNames() {
    return this.columnNames;
  }

  public List<String> getClassNames() {
    return Collections.unmodifiableList(classNames);
  }

  public List<JdbcType> getJdbcTypes() {
    return jdbcTypes;
  }

  public JdbcType getJdbcType(String columnName) {
    for (int i = 0 ; i < columnNames.size(); i++) {
      if (columnNames.get(i).equalsIgnoreCase(columnName)) {
        return jdbcTypes.get(i);
      }
    }
    return null;
  }

  public TypeHandler<?> getTypeHandler(Class<?> propertyType, String columnName) {
    TypeHandler<?> handler = null;
    Map<Class<?>, TypeHandler<?>> columnHandlers = typeHandlerMap.get(columnName);
    if (columnHandlers == null) {
      columnHandlers = new HashMap<>();
      typeHandlerMap.put(columnName, columnHandlers);
    } else {
      handler = columnHandlers.get(propertyType);
    }
    if (handler == null) {
      JdbcType jdbcType = getJdbcType(columnName);
      handler = typeHandlerRegistry.getTypeHandler(propertyType, jdbcType);
      if (handler == null || handler instanceof UnknownTypeHandler) {
        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) {
        handler = new ObjectTypeHandler();
      }
      columnHandlers.put(propertyType, handler);
    }
    return handler;
  }

  private Class<?> resolveClass(String className) {
    try {
      // #699 className could be null
      if (className != null) {
        return Resources.classForName(className);
      }
    } catch (ClassNotFoundException e) {
      // ignore
    }
    return null;
  }

  private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
    List<String> mappedColumnNames = new ArrayList<>();
    List<String> unmappedColumnNames = new ArrayList<>();
    //将列前缀修改为大写
    final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
    //加上前缀变成实际映射的列名
    final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
    //遍历数组查找是否在 mappedColumns 中
    for (String columnName : columnNames) {
      final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
      if (mappedColumns.contains(upperColumnName)) {
        mappedColumnNames.add(upperColumnName);
      } else {
        unmappedColumnNames.add(columnName);
      }
    }
    mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
    unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
  }
//返回ResultMap对象中明确映射的列名集合
  public List<String> getMappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
  //key是ResultMap的id和列前缀组成
    List<String> mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    if (mappedColumnNames == null) {
    //没有查找到指定ResultMap映射的列名,则加载后加入mappedColumnNamesMap
      loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
      mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    }
    return mappedColumnNames;
  }

  public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
    List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    if (unMappedColumnNames == null) {
      loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
      unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    }
    return unMappedColumnNames;
  }

  private String getMapKey(ResultMap resultMap, String columnPrefix) {
    return resultMap.getId() + ":" + columnPrefix;
  }

  private Set<String> prependPrefixes(Set<String> columnNames, String prefix) {
    if (columnNames == null || columnNames.isEmpty() || prefix == null || prefix.length() == 0) {
      return columnNames;
    }
    final Set<String> prefixed = new HashSet<>();
    for (String columnName : columnNames) {
      prefixed.add(prefix + columnName);
    }
    return prefixed;
  }
  
}

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

public class DefaultResultSetHandler implements ResultSetHandler {

  private static final Object DEFERED = new Object();

  private final Executor executor;
  private final Configuration configuration;
  private final MappedStatement mappedStatement;
  private final RowBounds rowBounds;
  private final ParameterHandler parameterHandler;
  //用户指定用来处理结果集的resultHandler对象
  private final ResultHandler<?> resultHandler;
  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 Object previousRowValue;

  // 储存过程相关的多ResultSet涉及的属性
  private final Map<String, ResultMapping> nextResultMaps = new HashMap<>();
  private final Map<CacheKey, List<PendingRelation>> pendingRelations = new HashMap<>();

  // 自动映射的缓存
  private final Map<String, List<UnMappedColumnAutoMapping>> autoMappingsCache = new HashMap<>();

  // 是否使用构造方法创建该结果对象
  private boolean useConstructorMappings;

  private static class PendingRelation {
    public MetaObject metaObject;
    public ResultMapping propertyMapping;
  }

  private static class UnMappedColumnAutoMapping {
    private final String column;
    private final String property;
    private final TypeHandler<?> typeHandler;
    private final boolean primitive;

    public UnMappedColumnAutoMapping(String column, String property, TypeHandler<?> typeHandler, boolean primitive) {
      this.column = column;
      this.property = property;
      this.typeHandler = typeHandler;
      this.primitive = primitive;
    }
  }

  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;
  }

  //
  // HANDLE OUTPUT PARAMETER
  //

  @Override
  public void handleOutputParameters(CallableStatement cs) throws SQLException {
    final Object parameterObject = parameterHandler.getParameterObject();
    final MetaObject metaParam = configuration.newMetaObject(parameterObject);
    final List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    for (int i = 0; i < parameterMappings.size(); i++) {
      final ParameterMapping parameterMapping = parameterMappings.get(i);
      if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
        if (ResultSet.class.equals(parameterMapping.getJavaType())) {
          handleRefCursorOutputParameter((ResultSet) cs.getObject(i + 1), parameterMapping, metaParam);
        } else {
          final TypeHandler<?> typeHandler = parameterMapping.getTypeHandler();
          metaParam.setValue(parameterMapping.getProperty(), typeHandler.getResult(cs, i + 1));
        }
      }
    }
  }

  private void handleRefCursorOutputParameter(ResultSet rs, ParameterMapping parameterMapping, MetaObject metaParam) throws SQLException {
    if (rs == null) {
      return;
    }
    try {
      final String resultMapId = parameterMapping.getResultMapId();
      final ResultMap resultMap = configuration.getResultMap(resultMapId);
      final ResultSetWrapper rsw = new ResultSetWrapper(rs, configuration);
      if (this.resultHandler == null) {
        final DefaultResultHandler resultHandler = new DefaultResultHandler(objectFactory);
        handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null);
        metaParam.setValue(parameterMapping.getProperty(), resultHandler.getResultList());
      } else {
        handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null);
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rs);
    }
  }

  //处理多结果集
  @Override
  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
        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);
  }

  @Override
  public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling cursor results").object(mappedStatement.getId());

    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();

    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    if (resultMapCount != 1) {
      throw new ExecutorException("Cursor results cannot be mapped to multiple resultMaps");
    }

    ResultMap resultMap = resultMaps.get(0);
    return new DefaultCursor<>(this, resultMap, rsw, rowBounds);
  }

  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;
  }

  private void closeResultSet(ResultSet rs) {
    try {
      if (rs != null) {
        rs.close();
      }
    } catch (SQLException e) {
      // ignore
    }
  }

  private void cleanUpAfterHandlingResultSet() {
    nestedResultObjects.clear();
  }

  private void validateResultMapsCount(ResultSetWrapper rsw, int resultMapCount) {
    if (rsw != null && resultMapCount < 1) {
      throw new ExecutorException("A query was run and no Result Maps were found for the Mapped Statement '" + mappedStatement.getId()
          + "'.  It's likely that neither a Result Type nor a Result Map was specified.");
    }
  }
//处理单个结果集
  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) {//如果resultHandler为空,实例化一个人默认的resultHandler
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
          //对ResultSet进行映射,映射结果暂存在resultHandler中
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          //将暂存在resultHandler中的映射结果,填充到multipleResults
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
          //使用指定的rusultHandler进行转换
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      //调用resultset.close()关闭结果集
      closeResultSet(rsw.getResultSet());
    }
  }

  @SuppressWarnings("unchecked")
  private List<Object> collapseSingleResultList(List<Object> multipleResults) {
    return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
  }

//映射结果集
  public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {//处理有嵌套resultmap的情况
      ensureNoRowBounds();
      checkResultHandler();
      //处理有嵌套的resultmap
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {//处理没有嵌套resultmap的情况
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }

  private void ensureNoRowBounds() {
    if (configuration.isSafeRowBoundsEnabled() && rowBounds != null && (rowBounds.getLimit() < RowBounds.NO_ROW_LIMIT || rowBounds.getOffset() > RowBounds.NO_ROW_OFFSET)) {
      throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely constrained by RowBounds. "
          + "Use safeRowBoundsEnabled=false setting to bypass this check.");
    }
  }

  protected void checkResultHandler() {
    if (resultHandler != null && configuration.isSafeResultHandlerEnabled() && !mappedStatement.isResultOrdered()) {
      throw new ExecutorException("Mapped Statements with nested result mappings cannot be safely used with a custom ResultHandler. "
          + "Use safeResultHandlerEnabled=false setting to bypass this check "
          + "or ensure your statement returns ordered data and set resultOrdered=true on it.");
    }
  }

  //简单映射处理
  private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
	//创建结果上下文,所谓的上下文就是专门在循环中缓存结果对象的
    DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    //1.根据分页信息,根据offset值定位到指定的记录
    skipRows(rsw.getResultSet(), rowBounds);
    //2.shouldProcessMoreRows判断是否需要映射后续的结果,实际还是翻页处理,避免超过limit
    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
      //3.进一步完善resultMap信息,主要是处理鉴别器的信息
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
      //4.读取resultSet中的一行记录并进行映射,转化并返回目标对象
      Object rowValue = getRowValue(rsw, discriminatedResultMap);
      //5.保存映射结果对象
      storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
    }
  }

  //保存映射结果对象
  private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
    if (parentMapping != null) {//如果是嵌套结果或嵌套查询,将对象保存至父对象
      linkToParents(rs, parentMapping, rowValue);
    } else {//普通映射则把对象保存至resultHandler和resultContext
      callResultHandler(resultHandler, resultContext, rowValue);
    }
  }

  @SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)
  private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
    resultContext.nextResultObject(rowValue);
    ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
  }

  private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) {
	 //检测上下文的stop状态,并检测映射的行数是否达到了limit的上限
    return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
  }

  //映射之前处理翻页信息
  private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
	//根据ResultSet的类型定位
    if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
      if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
        rs.absolute(rowBounds.getOffset());//直接定位到offset指定的记录
      }
    } else {
      //通过多次调用next移动到目标记录
      for (int i = 0; i < rowBounds.getOffset(); i++) {
        rs.next();
      }
    }
  }

  //读取resultSet中的一行记录并进行映射,转化并返回目标对象
  private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    //4.1 根据resultMap的type属性,实例化目标对象
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      //4.2 对目标对象进行封装得到metaObjcect,为后续的赋值操作做好准备
      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      boolean foundValues = this.useConstructorMappings;//取得是否使用构造函数初始化属性值
      if (shouldApplyAutomaticMappings(resultMap, false)) {//是否使用自动映射
    	 //4.3一般情况下 autoMappingBehavior默认值为PARTIAL,对未明确指定映射规则的字段进行自动映射
        foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
      }
       //4.4 映射resultMap中明确指定需要映射的列
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      //4.5 如果没有一个映射成功的属性,则根据<returnInstanceForEmptyRow>的配置返回null或者结果对象
      rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
    }
    return rowValue;
  }
//是否应用自动映射
  private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
    if (resultMap.getAutoMapping() != null) {
    //得到resultMap中的autoMapping属性值
      return resultMap.getAutoMapping();
    } else {
    //检测是否是嵌套查询或者嵌套映射
      if (isNested) {
        return AutoMappingBehavior.FULL == configuration.getAutoMappingBehavior();
      } else {
        return AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior();
      }
    }
  }

  private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
      throws SQLException {
	//从resultMap中获取明确需要转换的列名集合
    final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
    boolean foundValues = false;
    //获取ResultMapping集合
    final List<ResultMapping> 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 it
    	//如果属性通过另外一个resultMap映射,则忽略
        column = null;
      }
      if (propertyMapping.isCompositeResult()//如果是嵌套查询,column={prop1=col1,prop2=col2}
          || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))//基本类型映射
          || propertyMapping.getResultSet() != null) {//嵌套查询的结果
    	//获得属性值
        Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
        // issue #541 make property optional
        //获得属性名称
        final String property = propertyMapping.getProperty();
        if (property == null) {//属性名为空跳出循环
          continue;
        } else if (value == DEFERED) {//属性名为DEFERED,延迟加载的处理
          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为目标对象设置属性值
          metaObject.setValue(property, value);
        }
      }
    }
    return foundValues;
  }
  
  //获得属性值
  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);   // TODO is that OK?
      return DEFERED;
    } else {//基本类型直接通过typeHandler获取属性值
      final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
      final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      return typeHandler.getResult(rs, column);
    }
  }
  //获取resultSet中存在的,但是ResultMap中没有明确映射的列,填充至autoMapping中
  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());
        //检查该属性是否有setter方法
        if (property != null && metaObject.hasSetter(property)) {
          if (resultMap.getMappedProperties().contains(property)) {//如果该属性在resultMap中已经指定,则忽略此属性
            continue;
          }
          final Class<?> propertyType = metaObject.getSetterType(property);
          if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {//判断是否有匹配的typeHandler
            final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);//找到类型转换器
            //创建UnMappedColumnAutoMapping,并填充至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;
  }
  //对未明确指定映射规则的字段进行自动映射
  private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
	//获取resultSet中存在的,但是ResultMap中没有明确映射的列,填充至autoMapping中
    List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
    boolean foundValues = false;
    if (!autoMapping.isEmpty()) {
      //遍历autoMapping,通过自动匹配的方式为属性复制
      for (UnMappedColumnAutoMapping mapping : autoMapping) {
    	//通过typeHandler从resultset中拿值
        final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
        if (value != null) {
          foundValues = true;
        }
        if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
          //通过metaObject给属性赋值
          metaObject.setValue(mapping.property, value);
        }
      }
    }
    return foundValues;
  }


  private void linkToParents(ResultSet rs, ResultMapping parentMapping, Object rowValue) throws SQLException {
    CacheKey parentKey = createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(), parentMapping.getForeignColumn());
    List<PendingRelation> parents = pendingRelations.get(parentKey);
    if (parents != null) {
      for (PendingRelation parent : parents) {
        if (parent != null && rowValue != null) {
          linkObjects(parent.metaObject, parent.propertyMapping, rowValue);
        }
      }
    }
  }

  private void addPendingChildRelation(ResultSet rs, MetaObject metaResultObject, ResultMapping parentMapping) throws SQLException {
    CacheKey cacheKey = createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(), parentMapping.getColumn());
    PendingRelation deferLoad = new PendingRelation();
    deferLoad.metaObject = metaResultObject;
    deferLoad.propertyMapping = parentMapping;
    List<PendingRelation> relations = pendingRelations.computeIfAbsent(cacheKey, k -> new ArrayList<>());
    // issue #255
    relations.add(deferLoad);
    ResultMapping previous = nextResultMaps.get(parentMapping.getResultSet());
    if (previous == null) {
      nextResultMaps.put(parentMapping.getResultSet(), parentMapping);
    } else {
      if (!previous.equals(parentMapping)) {
        throw new ExecutorException("Two different properties are mapped to the same resultSet");
      }
    }
  }

  private CacheKey createKeyForMultipleResults(ResultSet rs, ResultMapping resultMapping, String names, String columns) throws SQLException {
    CacheKey cacheKey = new CacheKey();
    cacheKey.update(resultMapping);
    if (columns != null && names != null) {
      String[] columnsArray = columns.split(",");
      String[] namesArray = names.split(",");
      for (int i = 0; i < columnsArray.length; i++) {
        Object value = rs.getString(columnsArray[i]);
        if (value != null) {
          cacheKey.update(namesArray[i]);
          cacheKey.update(value);
        }
      }
    }
    return cacheKey;
  }
Mybatis源码解析之执行流程解析 Mybatis源码解析之核心类分析 Mybatis源码解析之初始化分析 本篇文章将在前两篇文章的基础上解析Mybatis执行sql的流程。 一、 映射集mappers解析 对于在原生mybatis中的sql执行,需要在配置文件中通过标签引入mapper.xml文件。前文已经提到,对于xml配置文件,mybatis在XMLConfigBuilder#parseConfiguration(Xnode)... 阅读详情

相关推荐

MyBatis源码剖析

在二级缓存的设计上,MyBatis大量的运用了装饰者模式,如CacheExecutor,以及各种Cache接口的装饰器。二级缓存实现了sqlSession之间的缓存数据共享,属于namespace级别;二级缓存具有丰富的缓存策略;二级缓存可由多个装饰器与基础缓存组合而成;二级缓存工作由一个缓存装饰器CacheExecutor和一个事务型预缓存TransactionalCache完成。问题:在开发过程中很多时候我们并不总是需要在获取用户信息的时候加载订单信息,此时就是我们所说的延迟加载。

Java就是我的道! 2206

Mybatis源码分析之(五)mapper如何将数据库数据转换成java对象的

下面的代码是PreparedStatementHandler中的 @Override public &amp;lt;E&amp;gt; Cursor&amp;lt;E&amp;gt; queryCursor(Statement statement) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ...

晓晨的博客 6412

深入解析MyBatis源码与实战

摘要:本文系统解析MyBatis核心架构与实现原理,涵盖配置解析、SQL执行、结果映射三大模块。通过动态SQL生成、缓存机制(一级/二级缓存)、插件开发(Interceptor接口)等典型示例,深入剖析源码实现。结合SpringBoot集成场景,展示CRUD操作、动态SQL拼接、事务管理、多数据源配置等实战方案,并给出批量操作优化(BatchExecutor)、类型处理器扩展等高级特性实现。特别说明流程图关键节点:从SqlSessionFactory构建到Executor执行,通过StatementHand

keny-大成的博客 1365

探究 MyBatis结果集映射机制背后的秘密(上)

这一讲我们重点介绍了结果集映射,这是 MyBatis 的核心实现之一。首先我们介绍了 ResultSetHandler 接口以及 DefaultResultSetHandler 这个默认实现,并讲解了单个结果集映射的入口:handleResultSet() 方法。

我乐了的博客 596

ICESat-2数据下载及处理分析随笔

ICESat-2全称是Ice,Cloud and land Elevation Satellite -2。看名字我们能知道它的任务是测ice,cloud和land elevation,而且这是一个Satellite,是一颗卫星。那么这里我们就要分清ICESat-2和ATLAS的关系,ICESat-2是星载平台,是一个space observatory,ATLAS是这个星载平台上搭载的单光子激光雷达系统。

u011322358的博客 1万+

STM32F103移植NuttX实战:从零搭建最小系统到串口Shell(附完整配置文件)

本文详细介绍了在STM32F103C8T6开发板上移植NuttX实时操作系统的完整过程,包括环境搭建、BSP配置、系统时钟初始化、驱动裁剪、编译烧录及串口Shell交互。通过实战指南帮助开发者快速掌握NuttX移植技巧,解决典型问题,适用于嵌入式系统开发。

tree8的博客 293

MyBatis源码学习(六)——在代码中调用Mapper接口时发生了什么

小结: 1,开始,MapperProxy.invoke(). 2,创建MapperMethod。包括创建SqlCommand,封装sql,创建MethodSignature,封装方法参数。 3,MapperMethod.execute() 4,区分增删改查,查询时,统一调用DefaultSqlSession.selectList() 5,CachingExecutor.query() 6,SimpleExecutor.doQuery()。创建StatementHandler,确定statemen

lkforce 1370

MATLAB绘图中文乱码终极解决方案:从方框到清晰显示的全面指南

本文提供了MATLAB绘图中文乱码的全面解决方案。针对中文显示为方框的问题,从单次绘图的临时字体设置,到通过修改默认配置实现一劳永逸,再到系统级字体排查与Java环境配置,给出了详细的步骤指南。文章还涵盖了字体名称指定、图像导出防丢失以及跨平台协作等高级技巧,帮助用户彻底解决中文乱码困扰。

weixin_29093017的博客 614

Mybatis查询结果为空时,为什么返回值为NULL或空集合?

文章来源:https://c1n.cn/6l7NH目录背景JDBC 中的 ResultSet 简介简单映射回归最初的问题:查询结果为空时的返回值结论背景一行数据记录如何映射成一个 Java 对象,这种映射机制是 MyBatis 作为 ORM 框架的核心功能之一,也是我们这篇文章需要学习的内容。开始前我们先看一个问题:你是否曾经在学习 Mybatis 的时候跟我有一样的疑问...

石杉的架构笔记 7198

Anylabeling与SAM协同:从智能标注到YOLO数据集构建的完整实践

本文详细介绍了Anylabeling与SAM协同标注的完整实践,从智能标注到YOLO数据集构建的全流程。通过结合SAM的自动分割能力和Anylabeling的交互式标注,显著提升标注效率,特别适合小样本、复杂形状和多类别标注场景。文章还提供了环境搭建、标注优化技巧以及YOLO格式转换的实用指南。

weixin_26873711的博客 461

手把手教你用Python分析OpenFAST仿真结果:pyDatView处理.out文件与数据可视化技巧

本文详细介绍了如何使用Python中的pyDatView工具分析OpenFAST仿真结果,包括数据加载、质量检查、时域与频域分析、多工况对比及自动化报告生成。通过优化技巧和可视化方法,帮助工程师高效处理.out文件,提升风机性能分析的准确性和效率。

weixin_29311017的博客 183

Mybatis-plus源码解读-自动装配与加载流程

本文分析了MyBatis-Plus的核心自动装配机制,重点介绍了MybatisPlusAutoConfiguration作为SpringBoot集成入口的关键作用。通过MybatisSqlSessionFactoryBuilder构建支持MyBatis-Plus特性的SqlSessionFactory,并详细解析了MybatisConfiguration、MybatisMapperRegistry等核心组件的扩展实现。文章还阐述了ISqlInjector接口及其实现类如何动态注入BaseMapper的CRU

源码老人的专栏 2038

IAR编译相同工程生产hex文件checksum不一致问题

IAR Checksum

xqw19891201的博客 1383

mybatis的执行流程

mybatis的执行流程如下代码: 第一步:通过Resources加载配置好的mybatis.xml配置文件。 Resources是ibatis.io包下面的类,也就是一个io流,用于读写文件,通过getResourceAsStream把xml文件加载进来,把配置文件解析为一个流。   第二步:然后看第二句话,这句话是关键。我们首先new了一个SqlSessionFactoryBuil...

落叶飘零z的博客 4万+

21天学会基于 Linux 的 NPU 固件开发--2.2 固件加载流程:request_firmware() 深度解析

本文深度解析Linux内核中NPU固件加载流程,重点剖析request_firmware()机制。内容涵盖:1)内核态与用户态的交互机制,包括同步/异步加载方式;2)NPU固件的特殊格式与加载逻辑;3)调试技巧与安全验证方案;4)提供最小化固件加载器实验。文章揭示了从文件系统到硬件初始化的完整链路,探讨了大模型时代面临的性能挑战与优化方案,如零拷贝加载技术,并提出了版本兼容性、断电恢复等关键问题的思考方向。

amy_mhd的博客 59

利用Gretna软件计算脑网络的全局属性和局部属性指标

使用Gretna,你可以计算脑网络的全局属性指标(如网络特征、小世界性和模块度),以及局部属性指标(如节点度和聚类系数)。通过Gretna,你可以计算脑网络的稀疏度,并进一步探索网络的特征和特性。Gretna是一款基于MATLAB平台的脑网络分析工具,它提供了丰富的函数和工具箱,用于计算和分析脑网络的结构和功能。在脑网络中,较高的稀疏度可能意味着神经系统的高效性和信息处理能力,因为较少的连接可以降低信息传递的成本。连接矩阵是一个N*N的矩阵,其中N表示节点的数量,矩阵的每个元素表示节点之间的连接强度。

code_program481的博客 1809

MyBatis源码剖析之延迟加载源码细节

在开发过程中很多时候我们并不需要总是在加载⽤户信息时就⼀定要加载他的订单信息。此时就是我们所说的延迟加载。举个栗⼦:在⼀对多中,当我们有⼀个⽤户,它有个100个订单在查询⽤户的时候,要不要把关联的订单查出来?在查询订单的时候,要不要把关联的⽤户查出来?回答在查询⽤户时,⽤户下的订单应该是,什么时候⽤,什么时候查询。在查询订单时,订单所属的⽤户信息应该是随着订单⼀起查询出来。延迟加载就是在需要⽤到数据时才进⾏加载,不需要⽤到数据时就不加载数据。延迟加载也称懒加载。

write less , do more 3334

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Mybatis源码解读-Configuration 配置信息加载流程源码分析

mybatis启动流程分析,解读源码如何从加载mybatis-config.xml配置到执行流程; 1、mybatis-config.xml配置文件加载 2、配置节点与Configuration全局配置映射 3、解读配置信息加载相关类梳理

源码老人的专栏 921

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Mybatis源码解析

mybatis的流程分析

WinnerBear的博客 5218

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Mybatis-SpringBoot源码解析之一:SqlSessionFactory加载流程

Mybatis-SpringBoot源码解析

zhengguofeng0328的博客 4178

Mybatis源码解析之配置加载(一)

Mybatis源码解析之配置加载(一) 用了好几年的mybatis了,但是很少来钻研mybatis原理所在,最近抽出空来,就把这一整套源码都研究了下,然后发现就是这些东西,mybatis没啥难度,于是决定把研究的这一整套写一个mybatis系列,记录一下,在这些完了以后,顺便写一个小的mybatis框架。 1. demo演示 还是从用法开始吧,就不开始就从源码说了,mybatis的conf配置文...

u012734441的专栏 3712

MyBatis 源码解析:Mapper 文件加载与解析

`Mapper` 文件是 MyBatis 中连接 SQL 和 Java 对象的桥梁。本文将通过自定义实现一个简单的 Mapper 文件加载与解析器,展示其工作原理,并对比 MyBatis 中的 Mapper 文件解析过程,帮助读者理解 MyBatis 内部的 Mapper 文件加载与解析机制。

捕风捉你 · 星桥API接入与大模型实战 1795

mybatis源码解析(二)-加载过程

mybatis源码解析(一)-开篇 mybatis源码解析(二)-加载过程 mybatis源码解析(三)-SqlSession.selectOne类似方法调用过程 mybatis源码解析(四)-Mapper方法调用过程 mybatis源码解析(五)-mybatis如何实现的事务控制 mybatis源码解析(六)-配合spring-tx实现事务的原理 mybatis源码解析(七)-当my...

civil的专栏 5658

MyBatis源码解析--加载流程(1)

分析完11个提供支持的模块,接下来就完整地debug一遍加载的流程。 首先看下简单的流程: 1.初始化会加载mybatis-config.xml配置文件、mapper映射配置文件以及Mapper接口中的注解信息,这些信息都会形成相应的对象并保存到Configuration中。 2.利用Configuration创建SqlSessionFactory对象。 3.通过SqlSessionFactory创建SqlSession对象进行相关的数据库操作。 那么首先就从加载mybatis-config.xml文件开始

jacobbbbbbbb的博客 325

Mybatis源码之--XML完整解析流程

MyBatis源码解析(一) 如何编译源码 仓库地址 1、idea中打开项目 2、等待解析依赖 备注:解析依赖的过程中有可能某些jar包的版本找不到,可自行在https://mvnrepository.com/ 找寻替代版本 XML解析 mybatis3支持注解方式声明SQL语句,但是最终经过mybatis内部解析,作用和使用XML定义是一样的。 只要你理解了XML的整个解析流程,其实就已经吃透了mybatis。 在阅读源码的过程中,mybatis的中文官网必须时刻去翻阅。特别是如下几章内容: 入

twx843571091的博客 4575

Mybatis核心配置文件加载流程详解

介绍Mybatis核心配置文件加载流程,如何对配置文件进行加载解析,如果对映射配置文件进行加载,都做了哪些事情

大数据开发、JAVA开发、人工智能AI 1409
上一篇: MyBatis源码解析--加载流程(8)
下一篇: MyBatis源码解析--加载流程(10)
jacobbbbbbbb
博客等级 码龄7年 6粉丝 66原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值