Mybatis-Mapper各類標簽封裝類源碼解析

Mybatis3.5.1源碼分析

  1. Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源碼解析
  2. Mybatis-Configuration源碼解析
  3. Mybatis-事務(wù)對象源碼解析
  4. Mybatis-數(shù)據(jù)源源碼解析
  5. Mybatis緩存策略源碼解析
  6. Mybatis-DatabaseIdProvider源碼解析
  7. Mybatis-TypeHandler源碼解析
  8. Mybatis-Reflector源碼解析
  9. Mybatis-ObjectFactory,ObjectWrapperFactory源碼分析
  10. Mybatis-Mapper各類標簽封裝類源碼解析
  11. Mybatis-XMLMapperBuilder,XMLStatmentBuilder源碼分析
  12. Mybatis-MapperAnnotationBuilder源碼分析
  13. [Mybatis-MetaObject,MetaClass源碼解析]http://www.itdecent.cn/p/f51fa552f30a)
  14. Mybatis-LanguageDriver源碼解析
  15. Mybatis-SqlSource源碼解析
  16. Mybatis-SqlNode源碼解析
  17. Mybatis-KeyGenerator源碼解析
  18. Mybatis-Executor源碼解析
  19. Mybatis-ParameterHandler源碼解析
  20. Mybatis-StatementHandler源碼解析
  21. Mybatis-DefaultResultSetHandler(一)源碼解析
  22. Mybatis-DefaultResultSetHandler(二)源碼解析
  23. Mybatis-ResultHandler,Cursor,RowBounds 源碼分析
  24. Mybatis-MapperProxy源碼解析
  25. Mybatis-SqlSession源碼解析
  26. Mybatis-Interceptor源碼解析

ParameterMap

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.mapping;

import java.util.Collections;
import java.util.List;

import org.apache.ibatis.session.Configuration;

/**
 * 參數(shù)映射信息封裝類,Mapper.xml的parameterMap標簽
 * @author Clinton Begin
 */
public class ParameterMap {

  /**
   * ParameterMap標簽的ID屬性
   */
  private String id;

  /**
   * ParamterMap標簽的type屬性,即參數(shù)類型
   */
  private Class<?> type;

  /**
   * ParamterMap標簽的所有paramter標簽,即參數(shù)的所有屬性列表
   */
  private List<ParameterMapping> parameterMappings;

  private ParameterMap() {
  }

  /**
   * ParameterMap的構(gòu)造器
   */
  public static class Builder {
    private ParameterMap parameterMap = new ParameterMap();

    /**
     *
     * @param configuration 這個參數(shù)已經(jīng)不用了,很有可能后面的版本去掉
     * @param id ParameterMap標簽的ID屬性
     * @param type ParamterMap標簽的type屬性,即參數(shù)類型
     * @param parameterMappings ParamterMap標簽的所有paramter標簽,即參數(shù)的所有屬性列表
     */
    public Builder(Configuration configuration, String id, Class<?> type, List<ParameterMapping> parameterMappings) {
      parameterMap.id = id;
      parameterMap.type = type;
      parameterMap.parameterMappings = parameterMappings;
    }

    /**
     * 返回參數(shù)類型
     */
    public Class<?> type() {
      return parameterMap.type;
    }

    public ParameterMap build() {
      //lock down collections 鎖定集合
      /**
       *  Collections.unmodifiableList方法會將傳進來的集合變成只讀的集合,
       *  只要改變該集合都會拋出UnsupportedOperationException。
       *  總之,可以保證對象的list內(nèi)容不被意料之外地修改,保證對象的封裝性
       */
      parameterMap.parameterMappings = Collections.unmodifiableList(parameterMap.parameterMappings);
      return parameterMap;
    }
  }

  public String getId() {
    return id;
  }

  public Class<?> getType() {
    return type;
  }

  public List<ParameterMapping> getParameterMappings() {
    return parameterMappings;
  }

}

ParameterMapping

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.mapping;

import java.sql.ResultSet;

import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * 參數(shù)映射
 * <P>
 *     對應(yīng)于Mapper.xml的:
 *     &lt;parameter property="" resultMap="" javaType="" typeHandler="" jdbcType="" mode="" scale=""&gt;&lt;/parameter&gt;
 * </P>
 * <p>
 *     參考博客:<a >https://blog.csdn.net/L_Sail/article/details/73824674</a>
 * </p>
 * @author Clinton Begin
 */
public class ParameterMapping {

  private Configuration configuration;

  /**
   * 屬性名
   */
  private String property;

  /**
   * mode 屬性允許你指定 IN,OUT 或 INOUT 參數(shù)。如果參數(shù)為 OUT 或 INOUT,
   * 參數(shù)對象屬性的真實值將會被改變,就像你在獲取輸出參數(shù)時所期望的那樣。
   * 如果 mode 為 OUT(或 INOUT),而且 jdbcType 為 CURSOR(也就是 Oracle 的 REFCURSOR),
   * 必須指定一個 resultMap 來映射結(jié)果集到參數(shù)類型
   */
  private ParameterMode mode;

  /**
   * java類型
   */
  private Class<?> javaType = Object.class;

  /**
   * jdbc類型
   */
  private JdbcType jdbcType;

  /**
   * 小數(shù)點后保留的位數(shù)
   */
  private Integer numericScale;

  /**
   * 類型處理器
   */
  private TypeHandler<?> typeHandler;

  /**
   * 結(jié)果映射ID
   */
  private String resultMapId;

  /**
   * jdbc類型名, SQL 結(jié)構(gòu)類型的完全限定名稱
   * <p>
   *     作用于CallableStatement.registerOutParameter (int parameterIndex, int sqlType, String typeName)方法的
   *     typeName參數(shù):<br/>
   *     在執(zhí)行存儲過程調(diào)用之前,必須顯式調(diào)用 registerOutParameter 為每個 OUT 參數(shù)注冊來自 java.sql.Types 的類型。
   *     對于用戶定義的參數(shù),還應(yīng)該提供該參數(shù)的完全限定 SQL 類型名稱,而 REF 參數(shù)則要求提供所引用類型的完全限定類型名稱。
   *     不需要類型代碼和類型名稱信息的 JDBC 驅(qū)動程序可以忽略它。不過,為了便于移植,應(yīng)用程序應(yīng)該為用戶定義的參數(shù)和 REF 參數(shù)提供這些值。
   * </p>
   */
  private String jdbcTypeName;

  /**
   * 表達式
   */
  private String expression;

  private ParameterMapping() {
  }

  public static class Builder {
    private ParameterMapping parameterMapping = new ParameterMapping();

    /**
     *
     * @param configuration mybatis的配置信息類
     * @param property 屬性名
     * @param typeHandler 屬性類型處理器
     */
    public Builder(Configuration configuration, String property, TypeHandler<?> typeHandler) {
      parameterMapping.configuration = configuration;
      parameterMapping.property = property;
      parameterMapping.typeHandler = typeHandler;
      parameterMapping.mode = ParameterMode.IN;
    }

    /**
     *
     * @param configuration mybatis的配置信息類
     * @param property 屬性名
     * @param javaType 屬性類型
     */
    public Builder(Configuration configuration, String property, Class<?> javaType) {
      parameterMapping.configuration = configuration;
      parameterMapping.property = property;
      parameterMapping.javaType = javaType;
      parameterMapping.mode = ParameterMode.IN;
    }

    /**
     * mode 屬性允許你指定 IN,OUT 或 INOUT 參數(shù)。如果參數(shù)為 OUT 或 INOUT,參數(shù)對象屬性的真實值將會被改變,就像你在獲取輸出參數(shù)時所期望的那樣。
     * 如果 mode 為 OUT(或 INOUT),而且 jdbcType 為 CURSOR(也就是 Oracle 的 REFCURSOR),你必須指定一個 resultMap 來映射結(jié)果集到參數(shù)類型
     */
    public Builder mode(ParameterMode mode) {
      parameterMapping.mode = mode;
      return this;
    }

    /**
     * 屬性類型
     */
    public Builder javaType(Class<?> javaType) {
      parameterMapping.javaType = javaType;
      return this;
    }

    /**
     * 屬性的jdbcType
     */
    public Builder jdbcType(JdbcType jdbcType) {
      parameterMapping.jdbcType = jdbcType;
      return this;
    }

    /**
     * 參數(shù)的屬性的小數(shù)點保留的位數(shù)
     */
    public Builder numericScale(Integer numericScale) {
      parameterMapping.numericScale = numericScale;
      return this;
    }

    /**
     * 參數(shù)的屬性對應(yīng)的結(jié)果映射ID
     */
    public Builder resultMapId(String resultMapId) {
      parameterMapping.resultMapId = resultMapId;
      return this;
    }

    /**
     * 參數(shù)的屬性對應(yīng)的TypeHandler
     */
    public Builder typeHandler(TypeHandler<?> typeHandler) {
      parameterMapping.typeHandler = typeHandler;
      return this;
    }

    /**
     * 參數(shù)的屬性對應(yīng)的jdbcType名
     */
    public Builder jdbcTypeName(String jdbcTypeName) {
      parameterMapping.jdbcTypeName = jdbcTypeName;
      return this;
    }

    /**
     * 參數(shù)的屬性正則表達式
     */
    public Builder expression(String expression) {
      parameterMapping.expression = expression;
      return this;
    }

    /**
     * 構(gòu)建ParameterMapping
     * <p>
     *     在調(diào)用其ParameterMapping.Builder的構(gòu)造方法時,就已經(jīng)構(gòu)建了parameterMapping。
     *     而方法時就是返回這個paramterMapping,在返回之前,會檢查有沒有設(shè)置TypeHandler,沒有的
     *     話,會嘗試根據(jù)javaType從TypeHandler注冊器中獲取對應(yīng)javaType的TypeHandler實例.
     *     然后 檢查一下業(yè)務(wù)需求所需要的屬性是否缺漏,缺漏的拋出異常。如
     * </p>
     * <ol style='margin-top:0px'>
     *     <li>如果是javaType為ResultSet的時候,需要設(shè)置resultMapId屬性,因為需要根據(jù)resultMap來映射獲取數(shù)據(jù)。
     *        否則拋出異常</li>
     *     <li> 如果是javaType為其他類型是,必須要有對應(yīng)其類型的typeHandler的實例。否則拋出異常</li>
     * </ol>
     */
    public ParameterMapping build() {
      //解析獲取TypeHandler實例
      resolveTypeHandler();
      // 檢查一下業(yè)務(wù)需求所需要的屬性是否缺漏,缺漏的拋出異常
      validate();
      return parameterMapping;
    }

    /**
     * 檢查一下業(yè)務(wù)需求所需要的屬性是否缺漏
     * <p>
     *     如果是javaType為ResultSet的時候,需要設(shè)置resultMapId屬性,因為需要根據(jù)resultMap來映射獲取數(shù)據(jù)。
     *     否則拋出異常
     * </p>
     * <p>
     *     如果是javaType為其他類型是,必須要有對應(yīng)其類型的typeHandler的實例。否則拋出異常
     * </p>
     */
    private void validate() {
      //當jdbcType為JdbcType.CURSOR是,javaType就會是ResultSet
      if (ResultSet.class.equals(parameterMapping.javaType)) {
        //如果是ResultSet就必須要有resultMapId,因為需要根據(jù)resultMap來映射獲取數(shù)據(jù)。
        if (parameterMapping.resultMapId == null) {
          throw new IllegalStateException("Missing resultmap in property '"
              + parameterMapping.property + "'.  "
              + "Parameters of type java.sql.ResultSet require a resultmap.");
        }
      } else {
        //javaType為其他類型時,必須要有對應(yīng)其類型的typeHandler的實例。
        if (parameterMapping.typeHandler == null) {
          throw new IllegalStateException("Type handler was null on parameter mapping for property '"
            + parameterMapping.property + "'. It was either not specified and/or could not be found for the javaType ("
            + parameterMapping.javaType.getName() + ") : jdbcType (" + parameterMapping.jdbcType + ") combination.");
        }
      }
    }

    /**
     * 解析獲取TypeHandler實例
     * <p>
     *     在typeHandler為null且有javaType的情況下才會嘗試從TypeHandler注冊器獲取對應(yīng)的TypeHandler實例
     * </p>
     */
    private void resolveTypeHandler() {
      if (parameterMapping.typeHandler == null && parameterMapping.javaType != null) {
        Configuration configuration = parameterMapping.configuration;
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        //只是獲取typeHandler,沒有注冊
        parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType);
      }
    }

  }

  public String getProperty() {
    return property;
  }

  /**
   * Used for handling output of callable statements.
   * @return
   */
  public ParameterMode getMode() {
    return mode;
  }

  /**
   * Used for handling output of callable statements.
   * @return
   */
  public Class<?> getJavaType() {
    return javaType;
  }

  /**
   * Used in the UnknownTypeHandler in case there is no handler for the property type.
   * @return
   */
  public JdbcType getJdbcType() {
    return jdbcType;
  }

  /**
   * Used for handling output of callable statements.
   * @return
   */
  public Integer getNumericScale() {
    return numericScale;
  }

  /**
   * Used when setting parameters to the PreparedStatement.
   * @return
   */
  public TypeHandler<?> getTypeHandler() {
    return typeHandler;
  }

  /**
   * Used for handling output of callable statements.
   * @return
   */
  public String getResultMapId() {
    return resultMapId;
  }

  /**
   * Used for handling output of callable statements.
   * @return
   */
  public String getJdbcTypeName() {
    return jdbcTypeName;
  }

  /**
   * Not used
   * @return
   */
  public String getExpression() {
    return expression;
  }

  @Override
  public String toString() {
    final StringBuilder sb = new StringBuilder("ParameterMapping{");
    //sb.append("configuration=").append(configuration); // configuration doesn't have a useful .toString()
    sb.append("property='").append(property).append('\'');
    sb.append(", mode=").append(mode);
    sb.append(", javaType=").append(javaType);
    sb.append(", jdbcType=").append(jdbcType);
    sb.append(", numericScale=").append(numericScale);
    //sb.append(", typeHandler=").append(typeHandler); // typeHandler also doesn't have a useful .toString()
    sb.append(", resultMapId='").append(resultMapId).append('\'');
    sb.append(", jdbcTypeName='").append(jdbcTypeName).append('\'');
    sb.append(", expression='").append(expression).append('\'');
    sb.append('}');
    return sb.toString();
  }
}

ResultMap

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.mapping;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.reflection.ParamNameUtil;
import org.apache.ibatis.session.Configuration;

/**
 * 結(jié)果映射信息封裝類,Mapper.xml的resultMap標簽信息封裝類對象
 * @author Clinton Begin
 */
public class ResultMap {
  private Configuration configuration;

  /**
   * resultMap標簽的Id屬性
   */
  private String id;
  /**
   * resultMap標簽的type屬性,resultMap的java類型
   */
  private Class<?> type;
  /**
   * 結(jié)果映射
   */
  private List<ResultMapping> resultMappings;
  /**
   * id結(jié)果映射
   */
  private List<ResultMapping> idResultMappings;
  /**
   * 構(gòu)造函數(shù)結(jié)果映射,
   * <p>
   *     存放著帶有構(gòu)造函數(shù)標記ResultFlag.CONSTRUCTOR的resultMapping
   * </p>
   */
  private List<ResultMapping> constructorResultMappings;
  /**
   * 屬性結(jié)果映射。
   * <p>將沒有構(gòu)造函數(shù)標記ResultFalg.CONSTRUCTOR的resultMapping添加到propertyResultMapping,
   * 沒有構(gòu)造函數(shù)標記ResultFalg.CONSTRUCTOR的resultMapping其實可以認為就是resultMap標簽
   * 下的result標簽和id標簽</p>
   */
  private List<ResultMapping> propertyResultMappings;
  /**
   * 映射列
   * <p>
   *     {@link #resultMappings}的所有column屬性集合,包括嵌套的reusltMapping的column,
   *     所保存的列是以大寫形式保存
   * </p>
   */
  private Set<String> mappedColumns;
  /**
   * 映射屬性,存放著所有resultMapping的所有propery
   */
  private Set<String> mappedProperties;
  /**
   * 鑒別器
   */
  private Discriminator discriminator;
  /**
   * 有嵌套映射結(jié)果,判斷reusltMapping中有沒有設(shè)置嵌套的 resultMap id
   */
  private boolean hasNestedResultMaps;
  /**
   * 有嵌套查詢,判斷reusltMapping中有沒有設(shè)置select屬性
   */
  private boolean hasNestedQueries;
  /**
   * 自動映射
   */
  private Boolean autoMapping;

  private ResultMap() {
  }

  public static class Builder {
    private static final Log log = LogFactory.getLog(Builder.class);

    private ResultMap resultMap = new ResultMap();

    /**
     *
     * @param configuration mybatis全局配置
     * @param id resultMap標簽的ID屬性
     * @param type resultMap標簽的type屬性,resultMap的java類型
     * @param resultMappings 結(jié)果元素映射集合
     */
    public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
      this(configuration, id, type, resultMappings, null);
    }

    /**
     *
     * @param configuration mybatis全局配置
     * @param id resultMap標簽的ID屬性
     * @param type resultMap標簽的type屬性,resultMap的java類型
     * @param resultMappings 結(jié)果元素映射集合
     * @param autoMapping 自動映射
     */
    public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings, Boolean autoMapping) {
      resultMap.configuration = configuration;
      resultMap.id = id;
      resultMap.type = type;
      resultMap.resultMappings = resultMappings;
      resultMap.autoMapping = autoMapping;
    }

    /**
     * 鑒別器
     * @param discriminator 鑒別器
     * @return
     */
    public Builder discriminator(Discriminator discriminator) {
      resultMap.discriminator = discriminator;
      return this;
    }

    /**
     * 返回resultMap的java類型
     * @return {@link #resultMap#type}
     */
    public Class<?> type() {
      return resultMap.type;
    }

    /**
     * 構(gòu)建resultMap
     *
     */
    public ResultMap build() {
      //驗證Id,Id是找到這個reusltMap的關(guān)鍵,不可以沒有。
      if (resultMap.id == null) {
        throw new IllegalArgumentException("ResultMaps must have an id");
      }
      //給定下面屬性一個空集合實例
      resultMap.mappedColumns = new HashSet<>();
      resultMap.mappedProperties = new HashSet<>();
      resultMap.idResultMappings = new ArrayList<>();
      resultMap.constructorResultMappings = new ArrayList<>();
      resultMap.propertyResultMappings = new ArrayList<>();
      //存放著帶有構(gòu)造函數(shù)標記的ResultMapping的property
      final List<String> constructorArgNames = new ArrayList<>();
      for (ResultMapping resultMapping : resultMap.resultMappings) {
        //有嵌套查詢標記更新
        resultMap.hasNestedQueries = resultMap.hasNestedQueries || resultMapping.getNestedQueryId() != null;
        //有嵌套ResultMap更新
        resultMap.hasNestedResultMaps = resultMap.hasNestedResultMaps || (resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null);
        /**
         * 將column添加到ResultMap.mappedColumn列表中,這個reusltMapping是個組合的reusltMapping,
         * 也會將組合的reusltMapping的column添加上去
         */
        final String column = resultMapping.getColumn();
        if (column != null) {
          resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
        } else if (resultMapping.isCompositeResult()) {
          for (ResultMapping compositeResultMapping : resultMapping.getComposites()) {
            final String compositeColumn = compositeResultMapping.getColumn();
            if (compositeColumn != null) {
              resultMap.mappedColumns.add(compositeColumn.toUpperCase(Locale.ENGLISH));
            }
          }
        }
        //將resultMapping的property添加到mappedProperties
        final String property = resultMapping.getProperty();
        if (property != null) {
          resultMap.mappedProperties.add(property);
        }
        //將帶有構(gòu)造函數(shù)標記ResultFlag.CONSTRUCTOR的resultMapping添加到constructorResultMapping
        if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
          resultMap.constructorResultMappings.add(resultMapping);
          //添加帶有構(gòu)造函數(shù)標記ResultFlag.CONSTRUCTOR的resultMapping的property
          if (resultMapping.getProperty() != null) {
            constructorArgNames.add(resultMapping.getProperty());
          }
        } else {
          /**
           *將沒有構(gòu)造函數(shù)標記ResultFalg.CONSTRUCTOR的resultMapping添加到propertyResultMapping,
           *  沒有構(gòu)造函數(shù)標記ResultFalg.CONSTRUCTOR的resultMapping其實可以認為就是resultMap標簽
           *  下的result標簽和id標簽
           */
          resultMap.propertyResultMappings.add(resultMapping);
        }
        //將帶有ID標記ResultFlag.ID的resultMapping添加的idResultMapping
        if (resultMapping.getFlags().contains(ResultFlag.ID)) {
          resultMap.idResultMappings.add(resultMapping);
        }
      }
      //TODO 為啥在沒有獲取到帶有ID標記ResultFlag.ID的resultMapping時就添加所有resultMaping?
      if (resultMap.idResultMappings.isEmpty()) {
        resultMap.idResultMappings.addAll(resultMap.resultMappings);
      }

      if (!constructorArgNames.isEmpty()) {
        /**
         * 獲取對應(yīng)construtorArgNames的構(gòu)造函數(shù)的參數(shù)名列表,這里忽略constructorArgNames的元素順序是否和
         * 對應(yīng)的構(gòu)造函數(shù)的參數(shù)順序?qū)?yīng),只對應(yīng)他們的類型和參數(shù)名
         */
        final List<String> actualArgNames = argNamesOfMatchingConstructor(constructorArgNames);
        /**
         * 這個異常觸發(fā)條件我覺得會很容易觸發(fā)的。
         * 1.在如果在mybatis-config.xml文件中設(shè)置了useActualParamName為false的時候,resultMap對應(yīng)的
         * java類型的構(gòu)造函數(shù)的參數(shù)又沒有加上@param注解時,就會導(dǎo)致和constructorArgNames匹配不上。
         */
        if (actualArgNames == null) {
          throw new BuilderException("Error in result map '" + resultMap.id
              + "'. Failed to find a constructor in '"
              + resultMap.getType().getName() + "' by arg names " + constructorArgNames
              + ". There might be more info in debug log.");
        }
        //讓constructorResultMapping根據(jù)構(gòu)造函數(shù)的參數(shù)名列表actualArgNames進行排序。
        resultMap.constructorResultMappings.sort((o1, o2) -> {
          int paramIdx1 = actualArgNames.indexOf(o1.getProperty());
          int paramIdx2 = actualArgNames.indexOf(o2.getProperty());
          return paramIdx1 - paramIdx2;
        });
      }
      // lock down collections
      resultMap.resultMappings = Collections.unmodifiableList(resultMap.resultMappings);
      resultMap.idResultMappings = Collections.unmodifiableList(resultMap.idResultMappings);
      resultMap.constructorResultMappings = Collections.unmodifiableList(resultMap.constructorResultMappings);
      resultMap.propertyResultMappings = Collections.unmodifiableList(resultMap.propertyResultMappings);
      resultMap.mappedColumns = Collections.unmodifiableSet(resultMap.mappedColumns);
      return resultMap;
    }

    /**
     * 匹配構(gòu)造函數(shù)的參數(shù)名稱
     * @param constructorArgNames 帶有構(gòu)造函數(shù)標記的ResultMapping的property集合
     * @return 返回已經(jīng)匹配驗證成功的構(gòu)造函數(shù)的參數(shù)名稱,在匹配驗證不成功的時候會返回null
     */
    private List<String> argNamesOfMatchingConstructor(List<String> constructorArgNames) {
      Constructor<?>[] constructors = resultMap.type.getDeclaredConstructors();
      for (Constructor<?> constructor : constructors) {
        Class<?>[] paramTypes = constructor.getParameterTypes();
        if (constructorArgNames.size() == paramTypes.length) {
          List<String> paramNames = getArgNames(constructor);
          /**
           * paramNames有可能返回args0,args1...,這個時候containsAll返回false,使得整個方法返回null。
           * 這里argTypesMatch方法并不會接收到paramNames為args0,args1...的情況,因為這里這里使用'&&',
           * 在containAll返回false的時候,是不會執(zhí)行argTypesMatch方法。
           */
          if (constructorArgNames.containsAll(paramNames)
              && argTypesMatch(constructorArgNames, paramTypes, paramNames)) {
            return paramNames;
          }
        }
      }
      return null;
    }

    /**
     * 匹配構(gòu)造函數(shù)的參數(shù)類型
     * @param constructorArgNames 帶有構(gòu)造函數(shù)標記的ResultMapping的property集合
     * @param paramTypes 構(gòu)造函數(shù)的所有參數(shù)的類型
     * @param paramNames 構(gòu)造函數(shù)的所有參數(shù)的參數(shù)名
     * @return 在 {@link #resultMap#constructorResultMappings} 的所有javaType與
     *    paramTypes取得的類型在類型上都相等的情況下就會返回true
     */
    private boolean argTypesMatch(final List<String> constructorArgNames,
        Class<?>[] paramTypes, List<String> paramNames) {
      for (int i = 0; i < constructorArgNames.size(); i++) {
        /**
         * 從constructorArgNames獲取i位置的property,再從paramNames獲取property對應(yīng)的位置,
         * 再從paramTypes取得該位置對應(yīng)的類型
         */
        Class<?> actualType = paramTypes[paramNames.indexOf(constructorArgNames.get(i))];
        //從構(gòu)造函數(shù)ResultMapping中獲取i位置的ResultMapping的javaType
        Class<?> specifiedType = resultMap.constructorResultMappings.get(i).getJavaType();
        /**
         * 這里判斷不對應(yīng)的情況下是不會拋出異常,只會打印log,并返回false,之所以沒有拋出異常時是因為
         * 統(tǒng)一交給了build方法拋出異常,這里只是簡單的調(diào)用Class#equal方法,Class并沒有對
         * equal方法進行重寫,所以equal方法相當于'=='的判斷結(jié)果,也就是說這里沒有考慮到父子類的情況。
         *
         * 這里不存在因為順序不一致而導(dǎo)致不相等的情況,因為actualType的通過constructorArgNames的位置獲取的,
         * 而constructorArgNames和resultMap.constructorResultMappings同一個循環(huán)遍歷下填充元素的,所以
         * constructorArgNames和resultMap.constructorResultMappings的元素位置是一致的。
         */
        if (!actualType.equals(specifiedType)) {
          if (log.isDebugEnabled()) {
            log.debug("While building result map '" + resultMap.id
                + "', found a constructor with arg names " + constructorArgNames
                + ", but the type of '" + constructorArgNames.get(i)
                + "' did not match. Specified: [" + specifiedType.getName() + "] Declared: ["
                + actualType.getName() + "]");
          }
          return false;
        }
      }
      return true;
    }

    /**
     * 獲取構(gòu)造函數(shù)的參數(shù)名
     * @param constructor {@link #type} 的構(gòu)造函數(shù)
     * @return
     * <ol style='margin-top:0px'>
     *    <li>嘗試獲取 {@code constructor} 的參數(shù)的 {@link Param} 作為參數(shù)名。</li>
     *    <li>在開啟了 {@link #configuration#useActualParamName} 情況下,嘗試獲取 {@code constructor} 的參數(shù)名作為參數(shù)名</li>
     *    <li>最后方案:用"arg" + 參數(shù)索引作為參數(shù)名</li>
     * </ol>
     */
    private List<String> getArgNames(Constructor<?> constructor) {
      List<String> paramNames = new ArrayList<>();
      List<String> actualParamNames = null;
      //獲取constructor每個參數(shù)的注解
      final Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
      int paramCount = paramAnnotations.length;
      for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
        String name = null;
        //找到@Param注解并把其value賦給name
        for (Annotation annotation : paramAnnotations[paramIndex]) {
          if (annotation instanceof Param) {
            name = ((Param) annotation).value();
            break;
          }
        }
        //在開啟了configuration#useActualParamName情況下,會使用在構(gòu)造函數(shù)的參數(shù)名賦值給name
        if (name == null && resultMap.configuration.isUseActualParamName()) {
          if (actualParamNames == null) {
            actualParamNames = ParamNameUtil.getParamNames(constructor);
          }
          //要保證構(gòu)造函數(shù)的參數(shù)名數(shù)量是大于參數(shù)索引paramIndex,否則在獲取參數(shù)名時會拋出異常
          if (actualParamNames.size() > paramIndex) {
            name = actualParamNames.get(paramIndex);
          }
        }
        /**
         * 1,在仍然獲取不到name的情況下,會以'arg'+paramIndex 作為name
         * 2.添加到paramNames
         */
        paramNames.add(name != null ? name : "arg" + paramIndex);
      }
      return paramNames;
    }
  }

  public String getId() {
    return id;
  }

  public boolean hasNestedResultMaps() {
    return hasNestedResultMaps;
  }

  public boolean hasNestedQueries() {
    return hasNestedQueries;
  }

  public Class<?> getType() {
    return type;
  }

  public List<ResultMapping> getResultMappings() {
    return resultMappings;
  }

  public List<ResultMapping> getConstructorResultMappings() {
    return constructorResultMappings;
  }

  public List<ResultMapping> getPropertyResultMappings() {
    return propertyResultMappings;
  }

  public List<ResultMapping> getIdResultMappings() {
    return idResultMappings;
  }

  public Set<String> getMappedColumns() {
    return mappedColumns;
  }

  public Set<String> getMappedProperties() {
    return mappedProperties;
  }

  public Discriminator getDiscriminator() {
    return discriminator;
  }

  public void forceNestedResultMaps() {
    hasNestedResultMaps = true;
  }

  public Boolean getAutoMapping() {
    return autoMapping;
  }

}

Discriminator

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.mapping;

import java.util.Collections;
import java.util.Map;

import org.apache.ibatis.session.Configuration;

/**
 * 鑒別器
 * 用于對一個查詢可能出現(xiàn)的不同結(jié)果集做鑒別,根據(jù)具體的條件為 resultMap 動態(tài)選擇返回值類型。
 * <p>
 *     eg:<br/>
 *     &lt;discriminator javaType="int" column="type"&gt;<br/>
 *        &nbsp;&nbsp;&lt;case value="1" resultType="Apple"/&gt;<br/>
 *        &nbsp;&nbsp;&lt;case value="2" resultType="Banana"/&gt;<br/>
 *     &lt;discriminator/&gt;
 * </p>
 *
 * <p>
 *     參考博客:<a >https://blog.csdn.net/weixin_36210698/article/details/83508113</a>
 * </p>
 * @author Clinton Begin
 */
public class Discriminator {

  /**
   * ResutMap標簽的屬性標簽封裝
   */
  private ResultMapping resultMapping;
  /**
   * case標簽信息封裝,映射
   * <p>
   *     key=case標簽的value屬性,value=命名空間+case標簽的resultMap屬性(reusltMapId),
   * </p>
   */
  private Map<String, String> discriminatorMap;

  Discriminator() {
  }

  public static class Builder {
    private Discriminator discriminator = new Discriminator();

    public Builder(Configuration configuration, ResultMapping resultMapping, Map<String, String> discriminatorMap) {
      discriminator.resultMapping = resultMapping;
      discriminator.discriminatorMap = discriminatorMap;
    }

    public Discriminator build() {
      assert discriminator.resultMapping != null;
      assert discriminator.discriminatorMap != null;
      assert !discriminator.discriminatorMap.isEmpty();
      //lock down map
      discriminator.discriminatorMap = Collections.unmodifiableMap(discriminator.discriminatorMap);
      return discriminator;
    }
  }

  public ResultMapping getResultMapping() {
    return resultMapping;
  }

  public Map<String, String> getDiscriminatorMap() {
    return discriminatorMap;
  }

  public String getMapIdFor(String s) {
    return discriminatorMap.get(s);
  }

}

MappedStatement

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.mapping;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;

/**
 * <p>
 *     Mapper.xml文件的select,delete,update,insert這些DML標簽的封裝類
 * </p>
 * <p>
 * MappedStatement類在Mybatis框架中用于表示XML文件中一個sql語句節(jié)點,
 * 即一個、或者標簽。Mybatis框架在初始化階段會對XML配置文件進行讀取
 * ,將其中的sql語句節(jié)點對象化為一個個MappedStatement對象。
 * </p>
 * @author Clinton Begin
 */
public final class MappedStatement {


  /**
   * 資源路徑,當前項目,有可能是java文件也有可能是.xml文件
   */
  private String resource;
  /**
   * mybatis全局配置信息
   */
  private Configuration configuration;
  /**
   * 節(jié)點中的id屬性加要命名空間
   */
  private String id;
  /**
   * <p>select標簽的fetchSize屬性</p>
   * <br/>
   * <p>
   *     這是嘗試影響驅(qū)動程序每次批量返回的結(jié)果行數(shù)和這個設(shè)置值相等。默認值為 unset(依賴驅(qū)動)。
   * </p>
   * <br/>
   * <p>
   *     假設(shè)fetchSize為5,查詢總記錄數(shù)為100,每批5次返回給你,最后結(jié)果還是總記錄數(shù),只是提高點查詢的速度而已
   * </p>
   * <br/>
   * <p>
   *     MySQL不支持fetchSize,默認為一次性取出所有數(shù)據(jù)。所以容易導(dǎo)致OOM,
   *     如果是Oracle的話就是默認取出fetchSize條數(shù)據(jù)。
   *     裸露JDBC防止OOM可以調(diào)用statement的enableStreamingResults方法,
   *     MyBatis應(yīng)該在&lt;select fetchSize="-2147483648"&gt;。
   * </p>
   */
  private Integer fetchSize;
  /**
   * 這個設(shè)置是在拋出異常之前,驅(qū)動程序等待數(shù)據(jù)庫返回請求結(jié)果的秒數(shù)。
   * 默認值為 unset(依賴驅(qū)動)。
   */
  private Integer timeout;
  /**
   * <ol>
   *     <li>STATEMENT:對應(yīng)于Statement對象,有SQL注入的風(fēng)險</li>
   *     <li>PREPARED:PreparedStatement,預(yù)編譯處理</li>
   *     <li>CALLABLE:CallableStatement一般調(diào)用存儲過程的時候使用</li>
   * </ol>
   * 默認是PREPARED
   */
  private StatementType statementType;
  /**
   *  其實的就是{@link java.sql.ResultSet}的常量,只不過Mybatis對其常量進行封裝成枚舉。
   * <ol>
   *     <li>DEFAULT:依賴驅(qū)動</li>
   *     <li>FORWARD_ONLY:結(jié)果集的游標只能向下滾動</li>
   *     <li>SCROLL_INSENSITIVE:結(jié)果集的游標可以上下移動,當數(shù)據(jù)庫變化時,當前結(jié)果集不變</li>
   *     <li>SCROLL_SENSITIVE:返回可滾動的結(jié)果集,當數(shù)據(jù)庫變化時,當前結(jié)果集同步改變</li>
   * </ol>
   */
  private ResultSetType resultSetType;
  /**
   *  構(gòu)建動態(tài)SQL
   */
  private SqlSource sqlSource;
  /**
   * 每條語句都對就一個緩存,如果有的話
   */
  private Cache cache;
  /**
   * 參數(shù)映射信息封裝類,Mapper.xml的parameterMap標簽
   */
  private ParameterMap parameterMap;
  /**
   * 結(jié)果映射信息封裝類,Mapper.xml的resultMap標簽 集合
   */
  private List<ResultMap> resultMaps;
  /**
   * 需要刷新緩存標記
   * <p>
   *      如果配置了flushCacheRequired為true,則會在執(zhí)行器執(zhí)行之前就清空本地一級緩存,換句話說就是關(guān)閉一級緩存功能
   * </p>
   */
  private boolean flushCacheRequired;
  /**
   * 啟用緩存標記
   */
  private boolean useCache;
  /**
   * select標簽
   * <p>
   * 如果為 true,就是假設(shè)包含了嵌套結(jié)果集或是分組了,這樣的話當返回一個主結(jié)果行的時候,
   * 就不會發(fā)生有對前面結(jié)果集的引用的情況。這就使得在獲取嵌套的結(jié)果集的時候不至于導(dǎo)致內(nèi)存不夠用。
   * 默認值:false。
   * </p>
   * <p>
   *     參考博客:<a >https://blog.csdn.net/isea533/article/details/51533296?utm_source=blogxgwz9</a>
   * </p>
   */
  private boolean resultOrdered;
  /**
   * SQL指令類型
   * <ol>
   *     <li>UNKNOWN:未知</li>
   *     <li>INSERT:插入</li>
   *     <li>UPDATE:修改</li>
   *     <li>DELETE:刪除</li>
   *     <li>SELECT:查詢</li>
   *     <li>FLUSH:刷新</li>
   * </ol>
   */
  private SqlCommandType sqlCommandType;
  /**
   * Key生成器
   */
  private KeyGenerator keyGenerator;
  /**
   * (僅對 insert 和 update 有用)唯一標記一個屬性,
   * MyBatis 會通過 getGeneratedKeys 的返回值或者通過 insert 語句的 selectKey 子元素設(shè)置它的鍵值,
   * 默認:unset。如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表。
   */
  private String[] keyProperties;
  /**
   *    (僅對 insert 和 update 有用)通過生成的鍵值設(shè)置表中的列名,
   *    這個設(shè)置僅在某些數(shù)據(jù)庫(像 PostgreSQL)是必須的,當主鍵列不是表中的第一列的時候需要設(shè)置。
   *    如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表
   */
  private String[] keyColumns;
  /**
   * 有嵌套映射結(jié)果標記,這個標記除了ParamterMap里面的resultMap外還保護ParamterMap里面的resultMap的resultMap
   */
  private boolean hasNestedResultMaps;
  /**
   * DML標簽中配置的databaseId屬性
   */
  private String databaseId;
  /**
   * 日志對象
   */
  private Log statementLog;
  /**
   * 語言驅(qū)動,默認是 {@link org.apache.ibatis.scripting.xmltags.XMLLanguageDriver}
   */
  private LanguageDriver lang;
  /**
   * 結(jié)果集
   */
  private String[] resultSets;

  MappedStatement() {
    // constructor disabled
  }

  public static class Builder {
    /**
     * 默認對象
     */
    private MappedStatement mappedStatement = new MappedStatement();

    /**
     *
     * @param configuration mybatis全局配置信息
     * @param id 節(jié)點中的id屬性加要命名空間
     * @param sqlSource 構(gòu)建動態(tài)SQL
     * @param sqlCommandType  SQL指令類型
     */
    public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
      mappedStatement.configuration = configuration;
      mappedStatement.id = id;
      mappedStatement.sqlSource = sqlSource;
      mappedStatement.statementType = StatementType.PREPARED;
      mappedStatement.resultSetType = ResultSetType.DEFAULT;
      mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList<>()).build();
      mappedStatement.resultMaps = new ArrayList<>();
      mappedStatement.sqlCommandType = sqlCommandType;
      mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
      String logId = id;
      //加上全局日志的前綴
      if (configuration.getLogPrefix() != null) {
        logId = configuration.getLogPrefix() + id;
      }
      mappedStatement.statementLog = LogFactory.getLog(logId);
      mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();
    }

    public Builder resource(String resource) {
      mappedStatement.resource = resource;
      return this;
    }

    public String id() {
      return mappedStatement.id;
    }

    public Builder parameterMap(ParameterMap parameterMap) {
      mappedStatement.parameterMap = parameterMap;
      return this;
    }

    public Builder resultMaps(List<ResultMap> resultMaps) {
      mappedStatement.resultMaps = resultMaps;
      for (ResultMap resultMap : resultMaps) {
        mappedStatement.hasNestedResultMaps = mappedStatement.hasNestedResultMaps || resultMap.hasNestedResultMaps();
      }
      return this;
    }

    public Builder fetchSize(Integer fetchSize) {
      mappedStatement.fetchSize = fetchSize;
      return this;
    }

    public Builder timeout(Integer timeout) {
      mappedStatement.timeout = timeout;
      return this;
    }

    public Builder statementType(StatementType statementType) {
      mappedStatement.statementType = statementType;
      return this;
    }

    public Builder resultSetType(ResultSetType resultSetType) {
      mappedStatement.resultSetType = resultSetType == null ? ResultSetType.DEFAULT : resultSetType;
      return this;
    }

    public Builder cache(Cache cache) {
      mappedStatement.cache = cache;
      return this;
    }

    public Builder flushCacheRequired(boolean flushCacheRequired) {
      mappedStatement.flushCacheRequired = flushCacheRequired;
      return this;
    }

    public Builder useCache(boolean useCache) {
      mappedStatement.useCache = useCache;
      return this;
    }

    public Builder resultOrdered(boolean resultOrdered) {
      mappedStatement.resultOrdered = resultOrdered;
      return this;
    }

    public Builder keyGenerator(KeyGenerator keyGenerator) {
      mappedStatement.keyGenerator = keyGenerator;
      return this;
    }

    public Builder keyProperty(String keyProperty) {
      mappedStatement.keyProperties = delimitedStringToArray(keyProperty);
      return this;
    }

    public Builder keyColumn(String keyColumn) {
      mappedStatement.keyColumns = delimitedStringToArray(keyColumn);
      return this;
    }

    public Builder databaseId(String databaseId) {
      mappedStatement.databaseId = databaseId;
      return this;
    }

    public Builder lang(LanguageDriver driver) {
      mappedStatement.lang = driver;
      return this;
    }

    public Builder resultSets(String resultSet) {
      mappedStatement.resultSets = delimitedStringToArray(resultSet);
      return this;
    }

    /**
     * @deprecated Use {@link #resultSets}
     */
    @Deprecated
    public Builder resulSets(String resultSet) {
      mappedStatement.resultSets = delimitedStringToArray(resultSet);
      return this;
    }

    public MappedStatement build() {
      assert mappedStatement.configuration != null;
      assert mappedStatement.id != null;
      assert mappedStatement.sqlSource != null;
      assert mappedStatement.lang != null;
      mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
      return mappedStatement;
    }
  }

  public KeyGenerator getKeyGenerator() {
    return keyGenerator;
  }

  public SqlCommandType getSqlCommandType() {
    return sqlCommandType;
  }

  public String getResource() {
    return resource;
  }

  public Configuration getConfiguration() {
    return configuration;
  }

  public String getId() {
    return id;
  }

  public boolean hasNestedResultMaps() {
    return hasNestedResultMaps;
  }

  public Integer getFetchSize() {
    return fetchSize;
  }

  public Integer getTimeout() {
    return timeout;
  }

  public StatementType getStatementType() {
    return statementType;
  }

  public ResultSetType getResultSetType() {
    return resultSetType;
  }

  public SqlSource getSqlSource() {
    return sqlSource;
  }

  public ParameterMap getParameterMap() {
    return parameterMap;
  }

  public List<ResultMap> getResultMaps() {
    return resultMaps;
  }

  public Cache getCache() {
    return cache;
  }

  public boolean isFlushCacheRequired() {
    return flushCacheRequired;
  }

  public boolean isUseCache() {
    return useCache;
  }

  public boolean isResultOrdered() {
    return resultOrdered;
  }

  public String getDatabaseId() {
    return databaseId;
  }

  public String[] getKeyProperties() {
    return keyProperties;
  }

  public String[] getKeyColumns() {
    return keyColumns;
  }

  public Log getStatementLog() {
    return statementLog;
  }

  public LanguageDriver getLang() {
    return lang;
  }

  public String[] getResultSets() {
    return resultSets;
  }

  /**
   * @deprecated Use {@link #getResultSets()}
   */
  @Deprecated
  public String[] getResulSets() {
    return resultSets;
  }

  /**
   * 構(gòu)建BoundSql對象
   * @param parameterObject 參數(shù)對象
   * @return BoundSql對象
   */
  public BoundSql getBoundSql(Object parameterObject) {
    //獲取可執(zhí)行的SQL
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    //將參數(shù)映射加入到BoundSql,重新構(gòu)建BoundSql對象
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }
    //檢查是否有嵌套的resultMap
    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }

  private static String[] delimitedStringToArray(String in) {
    if (in == null || in.trim().length() == 0) {
      return null;
    } else {
      return in.split(",");
    }
  }

}

ResultMapping

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.mapping;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * resultMap 子元素映射關(guān)系
 * @author Clinton Begin
 */
public class ResultMapping {

  /**
   * mybtais全局配置
   */
  private Configuration configuration;
  /**
   * 屬性名
   */
  private String property;
  /**
   * 列名
   */
  private String column;
  /**
   * 屬性的java類型
   */
  private Class<?> javaType;
  /**
   * 屬性的jdbc類型
   */
  private JdbcType jdbcType;
  /**
   * 類型轉(zhuǎn)換器
   */
  private TypeHandler<?> typeHandler;
  /**
   * 嵌套的resultMapId,【命名空間+resultMapId】
   */
  private String nestedResultMapId;
  /**
   * 嵌套的selectId,【命名空間+selectId】
   */
  private String nestedQueryId;
  /**
   * 不能為空的列名
    */
  private Set<String> notNullColumns;
  /**
   * SQL的列名前綴
   */
  private String columnPrefix;
  /**
   * 屬性標記
   */
  private List<ResultFlag> flags;
  /**
   * 組合ResultMaping列表
   */
  private List<ResultMapping> composites;
  /**
   * 指定用于加載復(fù)雜類型的結(jié)果集名字。
   */
  private String resultSet;
  /**
   * 指定外鍵對應(yīng)的列名
   */
  private String foreignColumn;
  /**
   * 是否懶加載
   */
  private boolean lazy;

  ResultMapping() {
  }

  public static class Builder {
    private ResultMapping resultMapping = new ResultMapping();

    /**
     *
     * @param configuration mybatis全局配置信息
     * @param property 屬性名
     * @param column 表列名
     * @param typeHandler 類型處理器
     */
    public Builder(Configuration configuration, String property, String column, TypeHandler<?> typeHandler) {
      this(configuration, property);
      resultMapping.column = column;
      resultMapping.typeHandler = typeHandler;
    }

    /**
     *
     * @param configuration mybatis全局配置信息
     * @param property 屬性名
     * @param column 表列名
     * @param javaType 屬性的java類型
     */
    public Builder(Configuration configuration, String property, String column, Class<?> javaType) {
      this(configuration, property);
      resultMapping.column = column;
      resultMapping.javaType = javaType;
    }

    /**
     * <ol>
     *     <li>{@link #resultMapping#flags}初始化空的{@link ArrayList}</li>
     *     <li>{@link #resultMapping#composites}初始化空的{@link ArrayList}</li>
     *     <li>@{@link #resultMapping#lazy}初始為mybatis全局配置文件的 {@link Configuration#lazyLoadingEnabled} 屬性設(shè)置</li>
     * </ol>
     * @param configuration mybatis全局配置信息
     * @param property 屬性名
     */
    public Builder(Configuration configuration, String property) {
      resultMapping.configuration = configuration;
      resultMapping.property = property;
      resultMapping.flags = new ArrayList<>();
      resultMapping.composites = new ArrayList<>();
      resultMapping.lazy = configuration.isLazyLoadingEnabled();
    }

    /**
     * 屬性的java類型
     */
    public Builder javaType(Class<?> javaType) {
      resultMapping.javaType = javaType;
      return this;
    }

    /**
     * 屬性的jdbc類型
     */
    public Builder jdbcType(JdbcType jdbcType) {
      resultMapping.jdbcType = jdbcType;
      return this;
    }

    /**
     * 嵌套的resultMapId
     */
    public Builder nestedResultMapId(String nestedResultMapId) {
      resultMapping.nestedResultMapId = nestedResultMapId;
      return this;
    }

    /**
     * 嵌套的selectId
     */
    public Builder nestedQueryId(String nestedQueryId) {
      resultMapping.nestedQueryId = nestedQueryId;
      return this;
    }

    /**
     * 集合的多結(jié)果集
     */
    public Builder resultSet(String resultSet) {
      resultMapping.resultSet = resultSet;
      return this;
    }

    /**
     * 指定外鍵對應(yīng)的列名
     */
    public Builder foreignColumn(String foreignColumn) {
      resultMapping.foreignColumn = foreignColumn;
      return this;
    }

    /**
     * 獲取指定的不為空才創(chuàng)建實例的列
     */
    public Builder notNullColumns(Set<String> notNullColumns) {
      resultMapping.notNullColumns = notNullColumns;
      return this;
    }

    /**
     * 列前綴
     */
    public Builder columnPrefix(String columnPrefix) {
      resultMapping.columnPrefix = columnPrefix;
      return this;
    }

    /**
     * 屬性標記
     */
    public Builder flags(List<ResultFlag> flags) {
      resultMapping.flags = flags;
      return this;
    }

    /**
     * 類型處理器
     */
    public Builder typeHandler(TypeHandler<?> typeHandler) {
      resultMapping.typeHandler = typeHandler;
      return this;
    }

    /**
     * 組合RequestMapping列表
     */
    public Builder composites(List<ResultMapping> composites) {
      resultMapping.composites = composites;
      return this;
    }

    /**
     * 懶加載
     */
    public Builder lazy(boolean lazy) {
      resultMapping.lazy = lazy;
      return this;
    }

    /**
     * 構(gòu)建出ResuatMapping實例。
     * <p>
     *     resultMapping在Builder初始化時已經(jīng)創(chuàng)建了,這個方法用于返回resultMapping實例。
     *     而且該方法會對flags,composites進行加鎖,以防止在其他調(diào)用者修改。在typeHandler
     *     沒有設(shè)置的情況下,也會在全局typeHandler注冊器中找到對應(yīng)javaType的typeHandlder實例。
     *     也驗證了一下屬性設(shè)置的合法性,將不合法的屬性設(shè)置拋出異常。
     * </p>
     * @return
     */
    public ResultMapping build() {
      // lock down collections 鎖住集合,讓flags,composites集合不能再發(fā)生任何更改。
      resultMapping.flags = Collections.unmodifiableList(resultMapping.flags);
      resultMapping.composites = Collections.unmodifiableList(resultMapping.composites);
      //當typeHandler為null,會根據(jù)javaType來獲取一個typeHandler實例賦值到typeHandler
      resolveTypeHandler();
      //驗證,不符合條件的,就會拋出異常中斷執(zhí)行。
      validate();
      return resultMapping;
    }

    /**
     * 驗證,不符合下面的某一條件都會拋出 {@link IllegalStateException} 異常。
     * <ol style='margin-top:0px'>
     *  <li>不能同時定義 嵌套的selectId 和 嵌套的resultMapId</li>
     *  <li>在沒有設(shè)置嵌套的selectId有沒有設(shè)置嵌套的resultMapId的情況下,typeHandler不應(yīng)該沒有設(shè)置或者獲取不到。</li>
     *  <li>列名是可選的在嵌套的resultMap組,但是不能在沒有設(shè)置嵌套的resultMapId又沒有設(shè)置組合的resultMap列表的情況下。</li>
     *  <li>有結(jié)果集結(jié)合的情況下,通過','隔開的列名與外鍵列名數(shù)保持一致。</li>
     * </ol>
     */
    private void validate() {
      // Issue #697: cannot define both nestedQueryId and nestedResultMapId
      //不能同時定義 嵌套的selectId 和 嵌套的resultMapId
      if (resultMapping.nestedQueryId != null && resultMapping.nestedResultMapId != null) {
        throw new IllegalStateException("Cannot define both nestedQueryId and nestedResultMapId in property " + resultMapping.property);
      }
      // Issue #5: there should be no mappings without typehandler
      //在沒有設(shè)置嵌套的selectId有沒有設(shè)置嵌套的resultMapId的情況下,typeHandler不應(yīng)該沒有設(shè)置或者獲取不到。
      if (resultMapping.nestedQueryId == null && resultMapping.nestedResultMapId == null && resultMapping.typeHandler == null) {
        throw new IllegalStateException("No typehandler found for property " + resultMapping.property);
      }
      // Issue #4 and GH #39: column is optional only in nested resultmaps but not in the rest
      //列名是可選的在嵌套的resultMap組,但是不能在沒有設(shè)置嵌套的resultMapId又沒有設(shè)置組合的resultMap列表的情況下。
      if (resultMapping.nestedResultMapId == null && resultMapping.column == null && resultMapping.composites.isEmpty()) {
        throw new IllegalStateException("Mapping is missing column attribute for property " + resultMapping.property);
      }
      //有結(jié)果集結(jié)合的情況下,通過','隔開的列名與外鍵列名數(shù)保持一致。
      if (resultMapping.getResultSet() != null) {
        int numColumns = 0;
        if (resultMapping.column != null) {
          numColumns = resultMapping.column.split(",").length;
        }
        int numForeignColumns = 0;
        if (resultMapping.foreignColumn != null) {
          numForeignColumns = resultMapping.foreignColumn.split(",").length;
        }
        if (numColumns != numForeignColumns) {
          throw new IllegalStateException("There should be the same number of columns and foreignColumns in property " + resultMapping.property);
        }
      }
    }

    /**
     * 解析TypeHadnler實例
     * <p>
     *     當 {@link #typeHandler} 的為null時,該方法就會根據(jù) {@link #javaType}去
     *     {@link #configuration}的TypeHandlerResgistry對應(yīng)的typeHandler實例并賦值到{@link #typeHandler}。
     * </p>
     */
    private void resolveTypeHandler() {
      if (resultMapping.typeHandler == null && resultMapping.javaType != null) {
        Configuration configuration = resultMapping.configuration;
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        resultMapping.typeHandler = typeHandlerRegistry.getTypeHandler(resultMapping.javaType, resultMapping.jdbcType);
      }
    }

    public Builder column(String column) {
      resultMapping.column = column;
      return this;
    }
  }

  public String getProperty() {
    return property;
  }

  public String getColumn() {
    return column;
  }

  public Class<?> getJavaType() {
    return javaType;
  }

  public JdbcType getJdbcType() {
    return jdbcType;
  }

  public TypeHandler<?> getTypeHandler() {
    return typeHandler;
  }

  public String getNestedResultMapId() {
    return nestedResultMapId;
  }

  public String getNestedQueryId() {
    return nestedQueryId;
  }

  public Set<String> getNotNullColumns() {
    return notNullColumns;
  }

  public String getColumnPrefix() {
    return columnPrefix;
  }

  public List<ResultFlag> getFlags() {
    return flags;
  }

  public List<ResultMapping> getComposites() {
    return composites;
  }

  /**
   * 是否存在組合ResultMaping列表
   * @return
   */
  public boolean isCompositeResult() {
    return this.composites != null && !this.composites.isEmpty();
  }

  public String getResultSet() {
    return this.resultSet;
  }

  public String getForeignColumn() {
    return foreignColumn;
  }

  public void setForeignColumn(String foreignColumn) {
    this.foreignColumn = foreignColumn;
  }

  public boolean isLazy() {
    return lazy;
  }

  public void setLazy(boolean lazy) {
    this.lazy = lazy;
  }

  /**
   * 判斷屬性名{@link #property}
   */
  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    ResultMapping that = (ResultMapping) o;

    if (property == null || !property.equals(that.property)) {
      return false;
    }

    return true;
  }

  /**
   * 判斷屬性名{@link #property}
   */
  @Override
  public int hashCode() {
    if (property != null) {
      return property.hashCode();
    } else if (column != null) {
      return column.hashCode();
    } else {
      return 0;
    }
  }

  @Override
  public String toString() {
    final StringBuilder sb = new StringBuilder("ResultMapping{");
    //sb.append("configuration=").append(configuration); // configuration doesn't have a useful .toString()
    sb.append("property='").append(property).append('\'');
    sb.append(", column='").append(column).append('\'');
    sb.append(", javaType=").append(javaType);
    sb.append(", jdbcType=").append(jdbcType);
    //sb.append(", typeHandler=").append(typeHandler); // typeHandler also doesn't have a useful .toString()
    sb.append(", nestedResultMapId='").append(nestedResultMapId).append('\'');
    sb.append(", nestedQueryId='").append(nestedQueryId).append('\'');
    sb.append(", notNullColumns=").append(notNullColumns);
    sb.append(", columnPrefix='").append(columnPrefix).append('\'');
    sb.append(", flags=").append(flags);
    sb.append(", composites=").append(composites);
    sb.append(", resultSet='").append(resultSet).append('\'');
    sb.append(", foreignColumn='").append(foreignColumn).append('\'');
    sb.append(", lazy=").append(lazy);
    sb.append('}');
    return sb.toString();
  }

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容