Mybatis-MapperAnnotationBuilder源碼分析

Mybatis3.5.1源碼分析

  1. Mybatis-SqlSessionFactoryBuilder姑原,XMLConfigBuilder,XPathParser源碼解析
  2. Mybatis-Configuration源碼解析
  3. Mybatis-事務對象源碼解析
  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.reibang.com/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源碼解析

MapperAnnotationBuilder

/**
 *    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.builder.annotation;

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;

import org.apache.ibatis.annotations.Arg;
import org.apache.ibatis.annotations.CacheNamespace;
import org.apache.ibatis.annotations.CacheNamespaceRef;
import org.apache.ibatis.annotations.Case;
import org.apache.ibatis.annotations.ConstructorArgs;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Lang;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Options.FlushCachePolicy;
import org.apache.ibatis.annotations.Property;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.ResultType;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.SelectKey;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.TypeDiscriminator;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.UpdateProvider;
import org.apache.ibatis.binding.BindingException;
import org.apache.ibatis.binding.MapperMethod.ParamMap;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.CacheRefResolver;
import org.apache.ibatis.builder.IncompleteElementException;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.cursor.Cursor;
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.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.mapping.Discriminator;
import org.apache.ibatis.mapping.FetchType;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultFlag;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.mapping.ResultSetType;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.parsing.PropertyParser;
import org.apache.ibatis.reflection.TypeParameterResolver;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.UnknownTypeHandler;

/**
 * 映射器注解構建器
 * @author Clinton Begin
 * @author Kazuki Shimizu
 */
public class MapperAnnotationBuilder {

  /**
   * SQL腳本的注解類型
   * <p>
   *     里面就是 {@link Select},{@link Insert},{@link Update},{@link Delete}
   * </p>
   */
  private static final Set<Class<? extends Annotation>> SQL_ANNOTATION_TYPES = new HashSet<>();
  /**
   * SQL Provider 注解類型
   * <p>
   *     里面就是 {@link SelectProvider},{@link UpdateProvider},{@link InsertProvider},{@link DeleteProvider}
   * </p>
   * <p>
   *     SQL Provider注解用法:<a >https://www.cnblogs.com/he-px/p/7134524.html</a>
   * </p>
   */
  private static final Set<Class<? extends Annotation>> SQL_PROVIDER_ANNOTATION_TYPES = new HashSet<>();

  /**
   * mybatis配置類
   */
  private final Configuration configuration;

  /**
   * 映射構建器助理
   */
  private final MapperBuilderAssistant assistant;
  /**
   * 當前接口類
   */
  private final Class<?> type;

  static {
    SQL_ANNOTATION_TYPES.add(Select.class);
    SQL_ANNOTATION_TYPES.add(Insert.class);
    SQL_ANNOTATION_TYPES.add(Update.class);
    SQL_ANNOTATION_TYPES.add(Delete.class);

    SQL_PROVIDER_ANNOTATION_TYPES.add(SelectProvider.class);
    SQL_PROVIDER_ANNOTATION_TYPES.add(InsertProvider.class);
    SQL_PROVIDER_ANNOTATION_TYPES.add(UpdateProvider.class);
    SQL_PROVIDER_ANNOTATION_TYPES.add(DeleteProvider.class);
  }

  /**
   *
   * @param configuration Mybatis全局配置信息
   * @param type 映射接口類
   */
  public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
    //假設傳進來的type.getName='bin.study.mapper.IUser',那么resource='bin/study/mapper/IUser.java (best guess)'
    String resource = type.getName().replace('.', '/') + ".java (best guess)";
    //新建一個映射構建器助理
    this.assistant = new MapperBuilderAssistant(configuration, resource);
    this.configuration = configuration;
    this.type = type;
  }

  /**
   * 解析映射接口類扔罪,解析mapper標簽下的所有方法和注解始腾,并對解析出來的信息加以封裝,
   * 然后添加到Mybatis全局配置信息中呕寝。然后重新解析Mybatis全局配置信息中未能完成解析的
   * Method重新解析
   */
  public void parse() {
    String resource = type.toString();//假設是 type='接口類IUser',resource就是'interface bin.study.mapper.IUser'
    //如果resource沒有被加載過
    if (!configuration.isResourceLoaded(resource)) {
      //加載對應接口的映射文件
      loadXmlResource();
      //將resource添加到已加載的資源集合中勋眯,以防止重新加載resource
      configuration.addLoadedResource(resource);
      //設置構建器助理的當前命名空間為type的包+類名
      assistant.setCurrentNamespace(type.getName());
      //解析CacheNamespace注解,構建一個Cache對象下梢,并保存到Mybatis全局配置信息中
      parseCache();
      //解析CacheNamespace注解客蹋,引用CacheRef對應的Cache對象
      parseCacheRef();
      //獲取結(jié)果中所有定義的方法
      Method[] methods = type.getMethods();
      for (Method method : methods) {
        try {
          // issue #237 如果method不是橋接方法,什么是橋接方法:https://www.cnblogs.com/zsg88/p/7588929.html
          if (!method.isBridge()) {
            //構建MapperStatement對象孽江,并添加到Mybatis全局配置信息中
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
          //當出現(xiàn)未完成元素時讶坯,添加構建Method時拋出異常的MethodResolver實例,到下個Mapper的解析時再次嘗試解析
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    //解析未完成解析的Method
    parsePendingMethods();
  }

  /**
   * 解析未完成解析的Method
   */
  private void parsePendingMethods() {
    //獲取未完成解析的Method集合
    Collection<MethodResolver> incompleteMethods = configuration.getIncompleteMethods();
    //同步加鎖岗屏,防止多線程情況下辆琅,重復解析Method
    synchronized (incompleteMethods) {
      //獲取未解析的Method集合迭代器
      Iterator<MethodResolver> iter = incompleteMethods.iterator();
      //遍歷未解析的Method集合
      while (iter.hasNext()) {
        try {
          //取出MethodResolver對象,重新解析这刷,構建MapperStatement對象婉烟,并添加到Mybatis全局配置信息中
          iter.next().resolve();
          //移除解析成功的MethodResolver對象
          iter.remove();
        } catch (IncompleteElementException e) {
          // This method is still missing a resource
          //還是出現(xiàn)未完成解析異常的MethodResolver,留到再下一個Mapper重新解析
        }
      }
    }
  }

  /**
   * 加載對應接口的映射文件
   * <p>
   *     平時有些項目會將映射文件XML與接口類放在同一個包下暇屋,這個方法就是作用于這種方式去加載映射文件XML
   * </p>
   */
  private void loadXmlResource() {
    // Spring may not know the real resource name so we check a flag
    // to prevent loading again a resource twice
    // this flag is set at XMLMapperBuilder#bindMapperForNamespace
    /**
     * 因為Spring框架可能不知道真正的資源名似袁,所以我們檢查一個標記去防止加載兩次資源。
     * 這個標記設置在{@link XMLMapperBuilder#bindMapperForNamespace()}
     */
    //判斷是否加載過映射文件XML
    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
      //假設type='interface bin.study.mapper.IUser',那么xmlResource='bin/study/mapper/IUser.xml'
      String xmlResource = type.getName().replace('.', '/') + ".xml";
      // #1347
      //嘗試加載映射文件
      InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
      //如果文件流為null
      if (inputStream == null) {
        // Search XML mapper that is not in the module but in the classpath.
        //搜索不在這模塊里但是在這個類路徑下的xml map
        try {
          inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
        } catch (IOException e2) {
          // ignore, resource is not required 忽略異常咐刨,因為這里的資源不是必須的
        }
      }
      //如果文件流不null
      if (inputStream != null) {
        //新建一個XML映射構建器
        XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
        /**
         * 解析Mapper.xml昙衅,解析mapper標簽下的所有標簽,并對解析出來的標簽信息加以封裝定鸟,
         * 然后添加到Mybatis全局配置信息中而涉。然后重新解析Mybatis全局配置信息中未能完成解析的
         * ResultMap標簽信息,CacheRef標簽信息仔粥,DML標簽信息
         */
        xmlParser.parse();
      }
    }
  }

  /**
   * 解析CacheNamespace注解婴谱,構建一個Cache對象蟹但,并保存到Mybatis全局配置信息中
   */
  private void parseCache() {
    //CacheNamespace 相當于Mapper.xml的<cache>標簽
    CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);
    //如果有配置CacheNamespace注解
    if (cacheDomain != null) {
      //獲取最大緩存數(shù)
      Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();
      //獲取刷新時間
      Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();
      // 將 cacheDomain.properties() 轉(zhuǎn)換成Properties
      Properties props = convertToProperties(cacheDomain.properties());
      //構建一個Cache對象,并保存到Mybatis全局配置信息中
      assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, cacheDomain.readWrite(), cacheDomain.blocking(), props);
    }
  }

  /**
   * 將 {@code properties} 轉(zhuǎn)換成Properties
   * @param properties 屬性數(shù)組
   * @return Properties對象
   */
  private Properties convertToProperties(Property[] properties) {
    //如果屬性數(shù)組為空
    if (properties.length == 0) {
      //返回null
      return null;
    }
    //新建一個Properties對象谭羔,用于保存properties的元素
    Properties props = new Properties();
    //遍歷properties
    for (Property property : properties) {
      /**
       * 先將property.value的有${...}形式的字符串轉(zhuǎn)換成Mybatis全局配置信息維護變量表的對應字符串,
       * eg:  '${first_name},${initial},${last_name}' => 'James,T,Kirk'
       * 再添加到props中
       */
      props.setProperty(property.name(),
          PropertyParser.parse(property.value(), configuration.getVariables()));
    }
    return props;
  }

  /**
   *  解析CacheNamespace注解华糖,引用CacheRef對應的Cache對象
   */
  private void parseCacheRef() {
    //CacheNamespaceRef相當于Mapper.xml的CacheRef標簽
    CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);
    //如果有配置CacheNamespace注解
    if (cacheDomainRef != null) {
      //獲取Mapper接口類
      Class<?> refType = cacheDomainRef.value();
      //獲取Mapper.xml命名空間
      String refName = cacheDomainRef.name();
      //如果沒有配置refType又沒有配置refName
      if (refType == void.class && refName.isEmpty()) {
        //拋出異常
        throw new BuilderException("Should be specified either value() or name() attribute in the @CacheNamespaceRef");
      }
      //如果有配置refType又配置了refName
      if (refType != void.class && !refName.isEmpty()) {
        //拋出異常
        throw new BuilderException("Cannot use both value() and name() attribute in the @CacheNamespaceRef");
      }
      //如果配置了refType就用refType,否則使用refName
      String namespace = (refType != void.class) ? refType.getName() : refName;
      try {
        /**
         * CacheRef對應的Cache對象瘟裸,會從Mybatis全局配信息中獲取 {@code namespace} 對應的Cache對象客叉,
         * 如果沒有找到,會拋出IncompleteElementException異常话告,找到會將Cache對象賦值給
         * currentCache兼搏,在構建MapperStatement對象時,將currentCache傳進去
         */
        assistant.useCacheRef(namespace);
        //捕捉未完成元素構建成封裝類異常
      } catch (IncompleteElementException e) {
        //新建一個CacheRefResolver保存namespace和構建器助理對象沙郭,待解析構建下一個Mapper時再次嘗試構建
        configuration.addIncompleteCacheRef(new CacheRefResolver(assistant, namespace));
      }
    }
  }

  /**
   * 構建ResultMap實例,包含discriminator的ResultMap實例佛呻,并添加到Mybatis全局配置信息,
   * 然后返回ResultMapId
   * @param method 方法對象
   * @return ResultMapId
   */
  private String parseResultMap(Method method) {
    //獲取 method 的返回類型
    Class<?> returnType = getReturnType(method);
    //獲取method配置的ConstructorArgs注解
    ConstructorArgs args = method.getAnnotation(ConstructorArgs.class);
    //獲取method配置的Results注解
    Results results = method.getAnnotation(Results.class);
    //獲取method配置的TypeDiscirminator注解
    TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
    /**
     * 生成method的resultMapId
     * <ol>
     *     <li>如果有配置Results注解,拼裝resultMapName=接口包名+類名+'.'+id屬性值</li>
     *     <li>如果方法有參數(shù) resultMapName=接口包名+類名+'.'+方法名+參數(shù)類型拼裝字符串</li>
     *     <li>如果方法無參數(shù)resultMapName=接口包名+類名+'.'+方法名+'-void'</li>
     * </ol>
     */
    String resultMapId = generateResultMapName(method);
    //構建ResultMap實例,包含discriminator的ResultMap實例病线,并添加到Mybatis全局配置信息
    applyResultMap(resultMapId, returnType, argsIf(args), resultsIf(results), typeDiscriminator);
    return resultMapId;
  }

  /**
   * 生成 {@code method} 的ResultMapName
   * @param method 方法對象
   * @return
   * <ol>
   *     <li>如果有配置Results注解吓著,拼裝resultMapName=接口包名+類名+'.'+id屬性值</li>
   *     <li>如果方法有參數(shù) resultMapName=接口包名+類名+'.'+方法名+參數(shù)類型拼裝字符串</li>
   *     <li>如果方法無參數(shù)resultMapName=接口包名+類名+'.'+方法名+'-void'</li>
   * </ol>
   */
  private String generateResultMapName(Method method) {
    //獲取method的Results注解
    Results results = method.getAnnotation(Results.class);
    //如果有配置results注解 且 results注解id不為空
    if (results != null && !results.id().isEmpty()) {
      //拼裝resultMapName ,resultMapName=接口包名+類名+'.'+id屬性值
      return type.getName() + "." + results.id();
    }
    //定義ResultMapName的后綴
    StringBuilder suffix = new StringBuilder();
    //遍歷方法的參數(shù)類型數(shù)組
    for (Class<?> c : method.getParameterTypes()) {
      //添加'-'到suffix
      suffix.append("-");
      //添加參數(shù)類型的類名到suffix
      suffix.append(c.getSimpleName());
    }
    //如果suffix為空字符串
    if (suffix.length() < 1) {
      //添加'-void'到suffix
      suffix.append("-void");
    }
    //拼裝resultMapName ,
    // 如果方法有參數(shù) resultMapName=接口包名+類名+'.'+方法名+參數(shù)類型拼裝字符串
    // 否則 resultMapName=接口包名+類名+'.'+方法名+'-void'
    return type.getName() + "." + method.getName() + suffix;
  }

  /**
   * 構建ResultMap實例,包含discriminator的ResultMap實例,并添加到Mybatis全局配置信息
   * @param resultMapId resultMapId
   * @param returnType 返回類型
   * @param args Arg注解集合送挑,相當于arg標簽绑莺,和idArg標簽
   * @param results result注解集合,相當于Result標簽
   * @param discriminator TypeDiscriminator注解惕耕,相當于discriminator標簽
   */
  private void applyResultMap(String resultMapId, Class<?> returnType, Arg[] args, Result[] results, TypeDiscriminator discriminator) {
    //定義一個ResultMapping類型集合纺裁,用于存放結(jié)果映射
    List<ResultMapping> resultMappings = new ArrayList<>();
    //應用構造函數(shù)參數(shù),將 args 的每個元素封裝成ResultMapping司澎,添加到 resultMapping 中
    applyConstructorArgs(args, returnType, resultMappings);
    //應用Result注解數(shù)組欺缘,將results的每個元素封裝成ResultMapping,添加到resultMapping中
    applyResults(results, returnType, resultMappings);
    //構建discriminator對象
    Discriminator disc = applyDiscriminator(resultMapId, returnType, discriminator);
    // TODO add AutoMappingBehaviour
    //構建ResultMap實例惭缰,并添加到Mybatis全局配置信息
    assistant.addResultMap(resultMapId, returnType, null, disc, resultMappings, null);
    //將鑒別器的Case配置信息封裝成ResultMap,并添加到Mybatis全局配置信息
    createDiscriminatorResultMaps(resultMapId, returnType, discriminator);
  }

  /**
   * 將鑒別器的Case配置信息封裝成ResultMap,并添加到Mybatis全局配置信息
   * @param resultMapId resultMapId
   * @param resultType 返回類型
   * @param discriminator TypeDiscriminator注解
   */
  private void createDiscriminatorResultMaps(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
    //如果方法有配置TypeDiscriminator注解
    if (discriminator != null) {
      //遍歷TypeDiscriminator注解的cases屬性
      for (Case c : discriminator.cases()) {
        //拼裝caseResultMapId
        String caseResultMapId = resultMapId + "-" + c.value();
        //定義一個ResultMapping類型集合浪南,用于存放結(jié)果映射
        List<ResultMapping> resultMappings = new ArrayList<>();
        // issue #136
        //應用構造函數(shù)參數(shù)笼才,將 c.constructArgs() 的每個元素封裝成ResultMapping漱受,添加到 resultMapping 中
        applyConstructorArgs(c.constructArgs(), resultType, resultMappings);
        //應用Result注解數(shù)組,將c.results()的每個元素封裝成ResultMapping骡送,添加到resultMapping中
        applyResults(c.results(), resultType, resultMappings);
        // TODO add AutoMappingBehaviour
        //構建ResultMap實例昂羡,并添加到Mybatis全局配置信息
        assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null, resultMappings, null);
      }
    }
  }

  /**
   * 構建discriminator對象
   * @param resultMapId resultMapId
   * @param resultType 返回類型
   * @param discriminator TypeDiscriminator注解
   * @return discriminator對象
   */
  private Discriminator applyDiscriminator(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
    //如果方法有配置TypeDiscriminator注解
    if (discriminator != null) {
      //獲取TypeDiscriminator注解的列名
      String column = discriminator.column();
      //如果沒有配置TypeDiscriminator注解的javaType,javaType就為String.class摔踱;否則javaType就是TypeDiscriminator注解的javaType
      Class<?> javaType = discriminator.javaType() == void.class ? String.class : discriminator.javaType();
      //如果沒有配置TypeDiscriminator注解的jdbcType虐先,jdbcType就為null;否則javaType就是TypeDiscriminator注解的jdbcType
      JdbcType jdbcType = discriminator.jdbcType() == JdbcType.UNDEFINED ? null : discriminator.jdbcType();
      //如果discriminator的typeHandler屬性值為UnknowTypeHandler,typeHandler就為null派敷;
      // 否則typeHandler就是discriminator的typeHandler屬性值
      @SuppressWarnings("unchecked")
      Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
              (discriminator.typeHandler() == UnknownTypeHandler.class ? null : discriminator.typeHandler());
      //獲取TypeDiscriminator注解的Case注解數(shù)組
      Case[] cases = discriminator.cases();
      //定義鑒別器映射蛹批,用于存儲Case注解的value,和resultMapId + "-" + value
      Map<String, String> discriminatorMap = new HashMap<>();
      //遍歷Case注解數(shù)組
      for (Case c : cases) {
        //獲取Case注解的value屬性值
        String value = c.value();
        //拼裝caseResultMapId
        String caseResultMapId = resultMapId + "-" + value;
        //將Case注解的value屬性值和caseResultMapId添加到discriminatorMap中
        discriminatorMap.put(value, caseResultMapId);
      }
      //構建鑒別器對象
      return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap);
    }
    return null;
  }

  /**
   * 構建MapperStatement對象撰洗,并添加到Mybatis全局配置信息中
   * @param method 方法對象
   */
  void parseStatement(Method method) {
    /**
     * 獲取參數(shù)類型,如果 {@code method} 的出現(xiàn)多個參數(shù)(不包含 RowBounds和ResultHandler)就返回ParamMap類型腐芍;
     * 如果只有一個參數(shù)(不包含 RowBounds和ResultHandler)就返回這一個參數(shù)類型
     */
    Class<?> parameterTypeClass = getParameterType(method);
    /**
     * 獲取語言驅(qū)動差导,由先獲取{@code method}配置的Lang注解指定的驅(qū)動類,如果沒有猪勇,使用默認的語言驅(qū)動類,
     * 默認的語言驅(qū)動為XMLLanguageDriver
     */
    LanguageDriver languageDriver = getLanguageDriver(method);
    //從 method 的注解中構建SQL源對象
    SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
    //如果SQL源對象不為null
    if (sqlSource != null) {
      //獲取method中的Options注解對象
      Options options = method.getAnnotation(Options.class);
      //拼裝mappedStatementId
      final String mappedStatementId = type.getName() + "." + method.getName();
      Integer fetchSize = null;
      Integer timeout = null;
      StatementType statementType = StatementType.PREPARED;
      ResultSetType resultSetType = null;
      //獲取當前 method 的SqlCommandType枚舉對象
      SqlCommandType sqlCommandType = getSqlCommandType(method);
      //是否是查詢指令
      boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
      //如果是查詢指令,刷新緩存為false,否則為true
      boolean flushCache = !isSelect;
      //如果是查詢指令渐裸,使用緩存為true;否則為false
      boolean useCache = isSelect;

      KeyGenerator keyGenerator;
      String keyProperty = null;
      String keyColumn = null;
      //如果sqlCommandType為插入指令類型 或者 sqlCommandType為修改指令類型
      if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
        // first check for SelectKey annotation - that overrides everything else
        // 譯文:先檢查SelectKey注解命浴,它將覆蓋其他的一切
        //獲取method配置的SelectKey注解對象
        SelectKey selectKey = method.getAnnotation(SelectKey.class);
        //如果selectKey不為null
        if (selectKey != null) {
          //構建 {@code selectKeyAnnotation} 對應的KeyGenerator對象,
          //并將KeyGenerator對象添加到Mybatis全局配置信息中
          keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
          //獲取seleckKey注解配置的填入將會被更新的參數(shù)對象的屬性的值
          keyProperty = selectKey.keyProperty();
          //如果沒有配置options注解
        } else if (options == null) {
          //useGeneratedKeys:允許 JDBC 支持自動生成主鍵椅您,需要驅(qū)動支持外冀。 如果設置為 true 則這個設置強制使用自動生成主鍵,盡管一些驅(qū)動不能支持但仍可正常工作(比如 Derby)
          //Jdbc3KeyGenerator:主要用于數(shù)據(jù)庫的自增主鍵掀泳,比如 MySQL锥惋、PostgreSQL;會將執(zhí)行SQL后從Statemenet中獲取主鍵放到參數(shù)對象對應的屬性里
          //NoKeyGenerator: 什么事情都不干开伏,里面是空實現(xiàn)方法
          //如果isUseGeneratedKeys為true膀跌,就使用Jdbc3KeyGenerator;否則使用NoKeyGenerator
          keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
          //如果selectKey為null 且 有配置options注解
        } else {
          //如果options注解中的userGeneratedKey屬性為true,就使用Jdbc3KeyGenerator;
          // 否則使用NoKeyGenerator
          keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
          //獲取options注解配置的填入將會被更新的參數(shù)對象的屬性的值
          keyProperty = options.keyProperty();
          //獲取options注解配置的匹配屬性的返回結(jié)果集中的列名稱
          keyColumn = options.keyColumn();
        }
        //如果sqlCommandType為select指令類型固灵,或者delete指令類型
      } else {
        //因為select和delete指令不會應該有KeyGenerator的功能捅伤,所以使用NoKeyGenerator
        keyGenerator = NoKeyGenerator.INSTANCE;
      }
      //如果有配置options注解
      if (options != null) {
        //FlushCachePolicy.TRUE 表示 不管是什么類型指令都刷新緩存
        //如果options注解的flushCache為FlushCachePolicy.TRUE
        if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
          //flushCach設置為true
          flushCache = true;
          //FlushCachePolicy.FALSE 表示 不管是什么類型指令都不刷新緩存
          //如果options注解的flushCache為FlushCachePolicy.FALSE
        } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
          //flushCach設置為false
          flushCache = false;
        }
        //options.useCache: 是否使用緩存,默認為true
        useCache = options.useCache();
        //options.fetchSize:這是一個給驅(qū)動的提示巫玻,嘗試讓驅(qū)動程序每次批量返回的結(jié)果行數(shù)和
        // 這個設置值相等丛忆。默認值為未設置(unset)(依賴驅(qū)動)。
        // 先判斷options.fetchSize取值范圍是否大于-1仍秤,或者等于Integer.MIN_VALUE熄诡。
        // 是就是引用options.fetchSize;否則為null
        fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
        //options.timeout:這個設置是在拋出異常之前,驅(qū)動程序等待數(shù)據(jù)庫返回請求結(jié)果的秒數(shù)诗力。
        // 默認值為未設置(unset)(依賴驅(qū)動)
        // 先判斷options.timeout的取值范圍是否大于-1凰浮,是就應用options.timeout;否則為null
        timeout = options.timeout() > -1 ? options.timeout() : null;
        /**
         * SQL腳本類型苇本,默認是 StatementType.PREPARED
         * <ol>
         *     <li>STATEMENT:對應于Statement對象袜茧,有SQL注入的風險</li>
         *     <li>PREPARED:PreparedStatement,預編譯處理</li>
         *     <li>CALLABLE:CallableStatement一般調(diào)用存儲過程的時候使用</li>
         * </ol>
         */
        statementType = options.statementType();
        /**
         * ResultSet的常量,默認是DEFAULT
         * <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>
         */
        resultSetType = options.resultSetType();
      }

      String resultMapId = null;
      //獲取method的ResultMap注解
      ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
      //如果有配置resultMap注解
      if (resultMapAnnotation != null) {
        //拼裝resultMap注解的配置的resultMapId,使用','進行拼接
        resultMapId = String.join(",", resultMapAnnotation.value());
        //如果是查詢指令
      } else if (isSelect) {
        //構建ResultMap實例,包含discriminator的ResultMap實例裳凸,并添加到Mybatis全局配置信息,
        //然后返回ResultMapId
        resultMapId = parseResultMap(method);
      }

      //構建MapperStatement對象贱鄙,并添加到Mybatis全局配置信息中
      assistant.addMappedStatement(
          mappedStatementId,
          sqlSource,
          statementType,
          sqlCommandType,
          fetchSize,
          timeout,
          // ParameterMapID
          null,
          parameterTypeClass,
          resultMapId,
          getReturnType(method),
          resultSetType,
          flushCache,
          useCache,
          // TODO gcode issue #577
          false,
          keyGenerator,
          keyProperty,
          keyColumn,
          // DatabaseID
          null,
          languageDriver,
          // ResultSets
          options != null ? nullOrEmpty(options.resultSets()) : null);
    }
  }

  /**
   * 獲取語言驅(qū)動,由先獲取{@code method}配置的Lang注解指定的驅(qū)動類姨谷,如果沒有贰逾,使用默認的語言驅(qū)動類,
   * 默認的語言驅(qū)動為XMLLanguageDriver
   * @param method 訪對象
   * @return {@code method}配置的Lang注解指定的驅(qū)動類菠秒,如果沒有疙剑,使用默認的語言驅(qū)動類,默認的語言驅(qū)動為XMLLanguageDriver
   */
  private LanguageDriver getLanguageDriver(Method method) {
    //獲取method配置的Lang注解
    Lang lang = method.getAnnotation(Lang.class);
    Class<? extends LanguageDriver> langClass = null;
    //如果method有加上Lang注解
    if (lang != null) {
      //獲取Lang注解配置的value賦值給langClass
      langClass = lang.value();
    }
    //從Mybatis全局配置信息中獲取語言驅(qū)動践叠,當langClass為null時言缤,會使用默認的語言驅(qū)動,默認的語言驅(qū)動為XMLLanguageDriver
    return configuration.getLanguageDriver(langClass);
  }

  /**
   * 獲取參數(shù)類型禁灼,如果 {@code method} 的出現(xiàn)多個參數(shù)(不包含 RowBounds和ResultHandler)就返回ParamMap類型管挟;
   * 如果只有一個參數(shù)(不包含 RowBounds和ResultHandler)就返回這一個參數(shù)類型
   * @param method 方法對象
   * @return 如果 {@code method} 的出現(xiàn)多個參數(shù)(不包含 RowBounds和ResultHandler)就返回ParamMap類型;
   *         如果只有一個參數(shù)(不包含 RowBounds和ResultHandler)就返回這一個參數(shù)類型
   */
  private Class<?> getParameterType(Method method) {
    Class<?> parameterType = null;
    //獲取參數(shù)類型數(shù)組
    Class<?>[] parameterTypes = method.getParameterTypes();
    //遍歷參數(shù)類型
    for (Class<?> currentParameterType : parameterTypes) {
      //如果currentParameterType不是RowBound的子類或是本身弄捕,又不是ResultHandler的子類或者本身
      if (!RowBounds.class.isAssignableFrom(currentParameterType) && !ResultHandler.class.isAssignableFrom(currentParameterType)) {
        //如果參數(shù)類型為null
        if (parameterType == null) {
          //將currentParameterType賦值給parameterType
          parameterType = currentParameterType;
          //參數(shù)類型不為null時僻孝,表示出現(xiàn)多個參數(shù)
        } else {
          // issue #135  使用ParamMap作為參數(shù)類型
         parameterType = ParamMap.class;
        }
      }
    }
    return parameterType;
  }

  /**
   * 獲取 {@code method} 的返回類型
   * @param method 方法對象
   * @return {@code method} 的返回類型
   */
  private Class<?> getReturnType(Method method) {
    //獲取method的返回類型
    Class<?> returnType = method.getReturnType();
    //解析獲取method在type中的返回類型
    Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);
    //如果reolvedReturnType是Class的子類
    if (resolvedReturnType instanceof Class) {
      //強轉(zhuǎn)resolvedReturnType為Class類型并賦值給returnType
      returnType = (Class<?>) resolvedReturnType;
      //如果returnType是數(shù)組
      if (returnType.isArray()) {
        //獲取returnType的元素類型再次賦值給returnType
        returnType = returnType.getComponentType();
      }
      // gcode issue #508 如果returnType為void類
      if (void.class.equals(returnType)) {
        //獲取method配置的ResultType注解
        ResultType rt = method.getAnnotation(ResultType.class);
        //如果有配置resultType注解
        if (rt != null) {
          //將resultType注解配置的結(jié)果對象類型賦值給returnType
          returnType = rt.value();
        }
      }
      //ParameterizedType : 參數(shù)化類型,參考博客:https://blog.csdn.net/JustBeauty/article/details/81116144
      //如果resolvedResturnType 是 ParameteizedType的子類
    } else if (resolvedReturnType instanceof ParameterizedType) {
      //將resolvedReturnType強轉(zhuǎn)成ParameterizedType類型守谓,然后賦值給parameterizedType
      ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType;
      // 獲取parameterizedType的原始類型并賦值給rawType
      Class<?> rawType = (Class<?>) parameterizedType.getRawType();
      //如果rawType為Collection的子類或是其本身 有或者是Cursor的子類或是其本身
      if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) {
        //獲取parameterizedType的參數(shù)化類型參數(shù)數(shù)組
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        //如果actualTypeArguments不為null且actualTypeArguments只有一個元素
        if (actualTypeArguments != null && actualTypeArguments.length == 1) {
          //獲取actualTypeArguments的第一個元素并賦值給returnTypeParameter
          Type returnTypeParameter = actualTypeArguments[0];
          //如果returnTypeParameter是Class類的子類或是其本身
          if (returnTypeParameter instanceof Class<?>) {
            //將returnTypeParameter強轉(zhuǎn)為Class類型然后賦值給returnType
            returnType = (Class<?>) returnTypeParameter;
            //如果returnTypeParameter是ParaeterizedType的子類或是其本身
          } else if (returnTypeParameter instanceof ParameterizedType) {
            // (gcode issue #443) actual type can be a also a parameterized type
            //將returnTypeParameter強轉(zhuǎn)為ParameterizedType類型賦值給returnType
            returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
            //GenericArrayType是Type的子接口穿铆,用于表示“泛型數(shù)組”,描述的是形如:A<T>[]
            // 或T[]的類型斋荞。其實也就是描述ParameterizedType類型以及TypeVariable類型的數(shù)組
            // 荞雏,即形如:classA<T>[][]、T[]等平酿。
            //如果returnTypeParameter是GenericArrayType的子類或是其本身
          } else if (returnTypeParameter instanceof GenericArrayType) {
            //將returnTypeParameter強轉(zhuǎn)為GenericArrayType類型賦值給componentType
            Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
            // (gcode issue #525) support List<byte[]>
            // 新建一個長度為0的componentType類型數(shù)組對象并獲取該數(shù)組對象的類賦值給returnType
            returnType = Array.newInstance(componentType, 0).getClass();
          }
        }
        //如果method有配置MapKey注解 且 rawType是Map的子類或是其本身
      } else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) {
        // (gcode issue 504) Do not look into Maps if there is not MapKey annotation
        //獲取parameterizedType的參數(shù)化類型參數(shù)數(shù)組
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        //如果actualTypeArguments不為null且actualTypeArguments只有兩個元素
        if (actualTypeArguments != null && actualTypeArguments.length == 2) {
          //獲取Map的value類型
          Type returnTypeParameter = actualTypeArguments[1];
          //如果returnTypeParameter是Class的子類或是其本身
          if (returnTypeParameter instanceof Class<?>) {
            //將returnTypeParameter強轉(zhuǎn)為Class類型賦值給returnType
            returnType = (Class<?>) returnTypeParameter;
            //如果returnTypeParameter是ParameterizedType的子類或是其本身
          } else if (returnTypeParameter instanceof ParameterizedType) {
            // (gcode issue 443) actual type can be a also a parameterized type
            //將returnTypeParameter強轉(zhuǎn)為ParameterizedType類型賦值給returnType
            returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
          }
        }
        //Optional:主要解決的問題是臭名昭著的空指針異常(NullPointerException)
        //如果rawType為Optional類
      } else if (Optional.class.equals(rawType)) {
        //獲取parameterizedType的參數(shù)化類型參數(shù)數(shù)組
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        //獲取參數(shù)化類型參數(shù)數(shù)組的第一個元素
        Type returnTypeParameter = actualTypeArguments[0];
        //如果returnTypeParameter是Class的子類或是其本身
        if (returnTypeParameter instanceof Class<?>) {
          //將returnTypeParameter強轉(zhuǎn)為Class類型賦值給returnType
          returnType = (Class<?>) returnTypeParameter;
        }
      }
    }

    return returnType;
  }

  /**
   * 從 {@code method} 的注解中構建SQL源對象
   * @param method 方法對象
   * @param parameterType 參數(shù)類型
   * @param languageDriver 語言驅(qū)動
   * @return ProviderSqlSource對象,ProviderSqlSource: SQL提供者的SQL源
   */
  private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
    try {
      //找出配置在method中的SQL指令類型注解凤优,就是 {@link Select},{@link Insert},{@link Update},{@link Delete}
      Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
      /**
       * 找出配置在 method中的SQL Provider 注解,就是
       *  {@link SelectProvider},{@link UpdateProvider},{@link InsertProvider},{@link DeleteProvider}
       */
      Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
      //如果有配置SQL指令類型
      if (sqlAnnotationType != null) {
        //如果有配置SQL provide注解
        if (sqlProviderAnnotationType != null) {
          //拋出異常蜈彼,不可以同時配置SQL指令類型注解和SQL Provider 注解
          throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
        }
        //獲取sqlAnnotionType注解對象
        Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
        //同過反射的方式取出SQL指令類型注解的value方法的返回值
        final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
        //將strings拼裝成完整的SQL腳本字符串筑辨,然后通過languageDriver構建SQL源
        return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
      } else if (sqlProviderAnnotationType != null) {
        //獲取sqlProviderAnnotationType注解對象
        Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
        //新建一個SQL提供者的SQL源
        return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);
      }
      return null;
    } catch (Exception e) {
      throw new BuilderException("Could not find value method on SQL annotation.  Cause: " + e, e);
    }
  }

  /**
   * 將 {@code strings} 拼裝成完整的SQL腳本字符串,然后通過 {@code languageDriver} 構建SQL源
   * @param strings sql腳本字符串數(shù)組
   * @param parameterTypeClass 參數(shù)類型
   * @param languageDriver 語言驅(qū)動
   * @return SQL源
   */
  private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
    //新建一個StringBuilder對象用于將strings轉(zhuǎn)換成字符串
    final StringBuilder sql = new StringBuilder();
    //遍歷strings
    for (String fragment : strings) {
      //添加fragemet到sql
      sql.append(fragment);
      //添加空格到sql
      sql.append(" ");
    }
    //通過語音驅(qū)動構建SQL源
    return languageDriver.createSqlSource(configuration, sql.toString().trim(), parameterTypeClass);
  }

  /**
   * 獲取 {@code method} 的SqlCommandType枚舉對象
   * @param method 方法對象
   * @return {@code method} 的SQL指令類型
   */
  private SqlCommandType getSqlCommandType(Method method) {
    /**
     * 找出配置在 {@code method} 中的SQL指令類型注解幸逆,
     * 就是 {@link Select},{@link Insert},{@link Update},{@link Delete}
     */
    Class<? extends Annotation> type = getSqlAnnotationType(method);
    //如果沒有配置SQL指令類型
    if (type == null) {
      /**
       * 找出配置在 {@code method} 中的SQL Provider 注解類型棍辕,就是
       * {@link SelectProvider},{@link UpdateProvider},{@link InsertProvider},{@link DeleteProvider}
       */
      type = getSqlProviderAnnotationType(method);
      //如果沒有配置SQL provider注解類型
      if (type == null) {
        //直接返回 未知的SQL指令類型
        return SqlCommandType.UNKNOWN;
      }
      //如果類型等于SelectProvider注解
      if (type == SelectProvider.class) {
        //類型就是為Select注解
        type = Select.class;
        //如果類型等于InsertProvider注解
      } else if (type == InsertProvider.class) {
        //類型就是為Insert注解
        type = Insert.class;
        //如果類型等于UpdateProvider注解
      } else if (type == UpdateProvider.class) {
        //類型就是Update注解
        type = Update.class;
        //如果類型等于DeleteProvider注解
      } else if (type == DeleteProvider.class) {
        //類型就是Delete注解
        type = Delete.class;
      }
    }
    //對應type對應的SqlCommandType枚舉對象
    return SqlCommandType.valueOf(type.getSimpleName().toUpperCase(Locale.ENGLISH));
  }

  /**
   * 找出配置在 {@code method} 中的SQL指令類型,就是 {@link Select},{@link Insert},{@link Update},{@link Delete}
   * @param method 方法對象
   * @return SQL指令類型秉颗,就是 {@link Select},{@link Insert},{@link Update},{@link Delete}
   */
  private Class<? extends Annotation> getSqlAnnotationType(Method method) {
    //找出 method 中第一個對應 SQL_ANNOTATION_TYPES 的元素的注解
    return chooseAnnotationType(method, SQL_ANNOTATION_TYPES);
  }

  /**
   * 找出配置在 {@code method} 中的SQL Provider 注解類型痢毒,就是
   * {@link SelectProvider},{@link UpdateProvider},{@link InsertProvider},{@link DeleteProvider}
   * @param method 方法對象
   * @return SQL Provider 注解類型送矩,就是
   *  {@link SelectProvider},{@link UpdateProvider},{@link InsertProvider},{@link DeleteProvider}
   */
  private Class<? extends Annotation> getSqlProviderAnnotationType(Method method) {
    ////找出 method 中第一個對應 SQL_PROVIDER_ANNOTATION_TYPES 的元素的注解
    return chooseAnnotationType(method, SQL_PROVIDER_ANNOTATION_TYPES);
  }


  /**
   * 找出 {@code method} 中第一個對應 {@code types}的元素的注解
   * @param method 方法對象
   * @param types 注解集合
   * @return {@code method} 中第一個對應 {@code types}的元素的注解
   */
  private Class<? extends Annotation> chooseAnnotationType(Method method, Set<Class<? extends Annotation>> types) {
    //遍歷注解列表
    for (Class<? extends Annotation> type : types) {
      //從method中獲取type注解對象
      Annotation annotation = method.getAnnotation(type);
      //如果method中有配置type注解
      if (annotation != null) {
        //直接返回注解
        return type;
      }
    }
    return null;
  }

  /**
   *  應用Result注解數(shù)組蚕甥,將 {@code results} 的每個元素封裝成ResultMapping,添加到 {@code resultMapping}中
   * @param results Result注解數(shù)組
   * @param resultType 返回類型
   * @param resultMappings 結(jié)果映射集合
   */
  private void applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) {
    //遍歷Result注解數(shù)組
    for (Result result : results) {
      //ResultFlag枚舉類型:ResultFlag.ID-表示是個表主鍵;ResultFlag.CONSTRUCTOR-表示是個構造函數(shù)參數(shù)
      //定義存放ResultFlag的集合
      List<ResultFlag> flags = new ArrayList<>();
      //如果result的id屬性為true
      if (result.id()) {
        //添加主鍵標記給flags
        flags.add(ResultFlag.ID);
      }
      //如果result的typeHandler屬性值為UnknowTypeHandler,typeHandler就為null栋荸;
      // 否則typeHandler就是result的typeHandler屬性值
      @SuppressWarnings("unchecked")
      Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
              ((result.typeHandler() == UnknownTypeHandler.class) ? null : result.typeHandler());
      //對Result注解配置信息封裝ResultMapping對象
      ResultMapping resultMapping = assistant.buildResultMapping(
          resultType,
          nullOrEmpty(result.property()),
          nullOrEmpty(result.column()),
          result.javaType() == void.class ? null : result.javaType(),
          result.jdbcType() == JdbcType.UNDEFINED ? null : result.jdbcType(),
          hasNestedSelect(result) ? nestedSelectId(result) : null,
          null,
          null,
          null,
          typeHandler,
          flags,
          null,
          null,
          isLazy(result));
      //將resultMapping添加到resultMapping集合中
      resultMappings.add(resultMapping);
    }
  }

  private String nestedSelectId(Result result) {
    String nestedSelect = result.one().select();
    if (nestedSelect.length() < 1) {
      nestedSelect = result.many().select();
    }
    if (!nestedSelect.contains(".")) {
      nestedSelect = type.getName() + "." + nestedSelect;
    }
    return nestedSelect;
  }

  private boolean isLazy(Result result) {
    boolean isLazy = configuration.isLazyLoadingEnabled();
    if (result.one().select().length() > 0 && FetchType.DEFAULT != result.one().fetchType()) {
      isLazy = result.one().fetchType() == FetchType.LAZY;
    } else if (result.many().select().length() > 0 && FetchType.DEFAULT != result.many().fetchType()) {
      isLazy = result.many().fetchType() == FetchType.LAZY;
    }
    return isLazy;
  }

  private boolean hasNestedSelect(Result result) {
    if (result.one().select().length() > 0 && result.many().select().length() > 0) {
      throw new BuilderException("Cannot use both @One and @Many annotations in the same @Result");
    }
    return result.one().select().length() > 0 || result.many().select().length() > 0;
  }

  /**
   * 應用構造函數(shù)參數(shù)菇怀,將 {@code args} 的每個元素封裝成ResultMapping凭舶,添加到 {@code resultMapping}中
   * @param args Arg注解數(shù)組
   * @param resultType 返回類型
   * @param resultMappings 結(jié)果映射集合
   */
  private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMapping> resultMappings) {
    //遍歷Arg注解數(shù)組
    for (Arg arg : args) {
      //ResultFlag枚舉類型:ResultFlag.ID-表示是個表主鍵;ResultFlag.CONSTRUCTOR-表示是個構造函數(shù)參數(shù)
      //定義存放ResultFlag的集合
      List<ResultFlag> flags = new ArrayList<>();
      //添加構建函數(shù)標記給flags
      flags.add(ResultFlag.CONSTRUCTOR);
      //如果arg的id屬性為true
      if (arg.id()) {
        //添加主鍵標記給flags
        flags.add(ResultFlag.ID);
      }
      //如果arg的typeHandler屬性值為UnknowTypeHandler,typeHandler就為null;
      // 否則typeHandler就是arg的typeHandler屬性值
      @SuppressWarnings("unchecked")
      Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>)
              (arg.typeHandler() == UnknownTypeHandler.class ? null : arg.typeHandler());
      //arg注解配置信息封裝ResultMapping對象
      ResultMapping resultMapping = assistant.buildResultMapping(
          resultType,
          nullOrEmpty(arg.name()),
          nullOrEmpty(arg.column()),
          arg.javaType() == void.class ? null : arg.javaType(),
          arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(),
          nullOrEmpty(arg.select()),
          nullOrEmpty(arg.resultMap()),
          null,
          nullOrEmpty(arg.columnPrefix()),
          typeHandler,
          flags,
          null,
          null,
          false);
      //將resultMapping添加到resultMapping集合中
      resultMappings.add(resultMapping);
    }
  }

  /**
   * 如果 {@code value} 為null或者為空字符串爱沟,都會返回null;否則返回 {@code value}
   * @param value 值
   */
  private String nullOrEmpty(String value) {
    return value == null || value.trim().length() == 0 ? null : value;
  }

  private Result[] resultsIf(Results results) {
    return results == null ? new Result[0] : results.value();
  }

  private Arg[] argsIf(ConstructorArgs args) {
    return args == null ? new Arg[0] : args.value();
  }

  /**
   * 構建 {@code selectKeyAnnotation} 對應的KeyGenerator對象帅霜,并將KeyGenerator對象添加到
   * Mybatis全局配置信息中
   * @param selectKeyAnnotation SelectKey注解對象
   * @param baseStatementId MappedStatement對象Id
   * @param parameterTypeClass 參數(shù)類型
   * @param languageDriver 語言驅(qū)動
   * @return {@code selectKeyAnnotation} 對應的KeyGenerator對象
   */
  private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
    //拼裝SelectKeyId,SelectKeyId=MappedStatement對象Id+'!selectKey'
    String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    //獲取selectKey注解配置的keyPropety屬性的java類型
    Class<?> resultTypeClass = selectKeyAnnotation.resultType();
    //獲取selectKey注解配置的SQL腳本類型
    StatementType statementType = selectKeyAnnotation.statementType();
    //獲取seleckKey注解配置的填入將會被更新的參數(shù)對象的屬性的值
    String keyProperty = selectKeyAnnotation.keyProperty();
    //獲取selectKey注解配置的 匹配屬性的返回結(jié)果集中的列名稱
    String keyColumn = selectKeyAnnotation.keyColumn();
    //獲取selectKey注解配置的 SQL 語句應被在插入語句的之前還是之后執(zhí)行標記
    boolean executeBefore = selectKeyAnnotation.before();

    // defaults 定義默認值
    //定義不使用緩存
    boolean useCache = false;
    //定義KeyGenerator為NoKeyGenerator.NoKeyGenerator:什么事情都不干呼伸,里面是空實現(xiàn)方法
    KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
    //fetchSize:這是一個給驅(qū)動的提示身冀,嘗試讓驅(qū)動程序每次批量返回的結(jié)果行數(shù)和這個設置值相等。 默認值為未設置(unset)(依賴驅(qū)動)
    Integer fetchSize = null;
    //timeout:這個設置是在拋出異常之前括享,驅(qū)動程序等待數(shù)據(jù)庫返回請求結(jié)果的秒數(shù)搂根。默認值為未設置(unset)(依賴驅(qū)動)。
    Integer timeout = null;
    //flushCache:將其設置為 true 后铃辖,只要語句被調(diào)用剩愧,都會導致本地緩存和二級緩存被清空,默認值:false娇斩。
    boolean flushCache = false;
    //參數(shù)映射
    String parameterMap = null;
    //結(jié)果映射
    String resultMap = null;
    /**
     * FORWARD_ONLY:結(jié)果集的游標只能向下滾動仁卷,
     * SCROLL_SENSITIVE:返回可滾動的結(jié)果集,當數(shù)據(jù)庫變化時犬第,當前結(jié)果集同步改變,
     * SCROLL_INSENSITIVE:結(jié)果集的游標可以上下移動锦积,當數(shù)據(jù)庫變化時,當前結(jié)果集不變
     *  DEFAULT:依賴驅(qū)動(等價于 unset) 中的一個歉嗓,默認值為 unset (依賴驅(qū)動)充包。
     */
    ResultSetType resultSetTypeEnum = null;
    /**
     * 將 selectKey注解的會被執(zhí)行的 SQL 字符串數(shù)組 拼裝成完整的SQL腳本字符串,
     * 然后通過languageDriver 構建SQL源
     */
    SqlSource sqlSource = buildSqlSourceFromStrings(selectKeyAnnotation.statement(), parameterTypeClass, languageDriver);
    //設置sql指令類型為SELECT
    SqlCommandType sqlCommandType = SqlCommandType.SELECT;
    //構建MapperStatement對象遥椿,并添加到全局配置信息中
    assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum,
        flushCache, useCache, false,
        keyGenerator, keyProperty, keyColumn, null, languageDriver, null);
    //檢查ID是否簡寫基矮,簡寫就應用當前命名空間+id
    id = assistant.applyCurrentNamespace(id, false);
    // 根據(jù)id 獲取對應的MappedStatement對象
    MappedStatement keyStatement = configuration.getMappedStatement(id, false);
    // 用于執(zhí)行selectKey標簽的SQL,將結(jié)果賦值到參數(shù)對象對應的屬性中
    SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);
    //添加 KeyGenerator對象到Mybatis全局配置信息
    configuration.addKeyGenerator(id, answer);
    return answer;
  }

}

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末冠场,一起剝皮案震驚了整個濱河市家浇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碴裙,老刑警劉巖钢悲,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異舔株,居然都是意外死亡莺琳,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門载慈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惭等,“玉大人,你說我怎么就攤上這事办铡〈亲觯” “怎么了琳要?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長秤茅。 經(jīng)常有香客問我稚补,道長,這世上最難降的妖魔是什么框喳? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任课幕,我火速辦了婚禮,結(jié)果婚禮上五垮,老公的妹妹穿的比我還像新娘撰豺。我一直安慰自己,他們只是感情好拼余,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布污桦。 她就那樣靜靜地躺著,像睡著了一般匙监。 火紅的嫁衣襯著肌膚如雪凡橱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天亭姥,我揣著相機與錄音稼钩,去河邊找鬼。 笑死达罗,一個胖子當著我的面吹牛坝撑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播粮揉,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼巡李,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了扶认?” 一聲冷哼從身側(cè)響起侨拦,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辐宾,沒想到半個月后狱从,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡叠纹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年季研,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片誉察。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡与涡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情递沪,我是刑警寧澤豺鼻,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布综液,位于F島的核電站款慨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏谬莹。R本人自食惡果不足惜檩奠,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望附帽。 院中可真熱鬧埠戳,春花似錦、人聲如沸蕉扮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喳钟。三九已至屁使,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奔则,已是汗流浹背蛮寂。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留易茬,地道東北人酬蹋。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像抽莱,于是被迫代替她去往敵國和親范抓。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容