MyBatis运行原理

MyBatis运行原理

前言

  • 第一次阅读源码,或许有那么一点点参考意见,仅作个人记录,后续会再改一改。
  • Mybati 版本号: 3.5.13,配置文件是以XML的方式
  • 请配合标题查看代码中有注释的部分,笔者只贴部分运行的代码,其他在运行中没有使用到的代码笔者删除,不是笔者不想贴,是太长了影响阅读。请各位读者多复制类名方法名去寻找或者按照debug流程,还有一定要看markdown的大纲

配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/test?allowMultiQueries=true"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 映射文件地址,在SpringBoot中是 mapper-locations: classpath:mapper/**/*.xml-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
        <mapper resource="mapper/DeptMapper.xml"/>
    </mappers>

</configuration>

运行dome

  • debug断点打字 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream) 进入build方法
/**
 * <P>运行原理配用代码</P>
 *
 * @author unknown
 * @since 2023/10/10 01:06
 * {@link MybatisSourceCodeExplanation} 文字解析
 */
public class OperatingPrincipleTest {
    public static void main(String[] args) throws IOException {

        // 读取Mybatis配置文件.获取输入流
        InputStream resourceAsStream = getResourceAsStream("mybatis-config.xml");
        /*
         构建 SqlSessionFactorBean 类,在下面的 SqlSessionFactoryBuilder 标题中解释了如何构建 SqlSessionFactory 类
         其实 SqlSessionFactory 类中值有一个类 Configuration , 在构建的过程中都是在为 Configuration 类进行赋值
         */
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 获取SqlSession.设置自动提交数据
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        // 获取Mapper接口类
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User byId = mapper.findById(2);
        System.out.println("查询数据: " + byId);
        // 关闭连接
        sqlSession.close();
    }
}

解析运行流程

SqlSessionFactoryBuilder
  • 解析mybatis-config.xml配置文件,为Configuration类设置默认值(也就是Mybatis中有些默认配置)

  • 解析Mapper.xml文件,添加到 Configuration类中的一个Map集合

    • 以增删改查标签中的ID属性为keyMappedStatementvalueMappedStatement 中包含了每一个增改删除标签。

    • protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>
      
  • 解析Mapper.xml文件中 namespace 的值,添加到 Configuration 类中的 MapperRegistry 对象中

    • protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
      
    • MapperRegistry对象包含了项目中所有 namespace 的值

  • 至于如何读取配置文件信息,在源码的 77行 打下断点

// 在SqlSessionFactoryBuilder类中,对 build 方法进行了重载,而 build 方法最终返回 SqlSessionFactory 对象
public class SqlSessionFactoryBuilder {

    // reader: 读取配置文件流
    public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

    // reader: 读取配置文件流
    //  environment: Mybatis配置文件中的environment标签,在mybatis-config.xml配置文件中可以看到是关于数据库信息的配置
  public SqlSessionFactory build(Reader reader, String environment) {
    return build(reader, environment, null);
  }

    // reader: 读取配置文件流
    // properties:以 Properties 文件形式存在的配置文件
  public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }
	// reader: 读取配置文件流
    //  environment: Mybatis配置文件中的environment标签,在mybatis-config.xml配置文件中可以看到是关于数据库信息的配置
    // properties:以 Properties 文件形式存在的配置文件
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        if (reader != null) {
          reader.close();
        }
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
    
    // inputStream: Mybatis配置文件流
  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

    //  inputStream: Mybatis配置文件流
    //  environment: Mybatis配置文件中的environment标签,在mybatis-config.xml配置文件中可以看到是关于数据库信息的配置
  public SqlSessionFactory build(InputStream inputStream, String environment) {
    return build(inputStream, environment, null);
  }
    //  inputStream: Mybatis配置文件流
    //  environment: Mybatis配置文件中的environment标签,在mybatis-config.xml配置文件中可以看到是关于数据库信息的配置
  public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    return build(inputStream, null, properties);
  }
    //  inputStream: Mybatis配置文件流
    //  environment: Mybatis配置文件中的environment标签,在mybatis-config.xml配置文件中可以看到是关于数据库信息的配置
	// properties:以 Properties 文件形式存在的配置文件
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        // 创建XMLConfigBuilder对象
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        //>>>解析配置文件(具体看下个标题),调用最后一个build方法,返回 SqlSessionFactory 的默认实现类
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        if (inputStream != null) {
          inputStream.close();
        }
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

    // 返回 SqlSessionFactory 的默认实现类
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
}

parser.parse()
  • 构建XMLConfigBuilder对象,在构造方法中调用parse方法解析mybatis-config.xml

    •   private XMLConfigBuilder(Class<? extends Configuration> configClass, XPathParser parser, String environment,
            Properties props) {
          super(newConfig(configClass));
          ErrorContext.instance().resource("SQL Mapper Configuration");
          this.configuration.setVariables(props);
            // 在解析配置文件时,判断是否已解析
          this.parsed = false;
          this.environment = environment;
            // 调用类中的parser
          this.parser = parser;
        }
      
  • 读取mybatis-config.xml文件的 configuration标签

    • parser.evalNode("/configuration"): 返回XNode对象,该对象包含mybatis-config.xml文件的全部内容
  • 读取XNode对象中的properties标签

    • propertiesElement(root.evalNode("properties"));
  • 读取settings标签,转为Properties对象

    • Properties settings = settingsAsProperties(root.evalNode("settings"));
  • 调用settingsElement方法,当配置文件内有些属性是没有配置的时候,设置默认属性值并保存在 configuration对象中

  • settingsElement(settings);

  • 调用mapperElement方法,解析业务接口对应的Mapper文件,判断引入方式, 获取文件地址

    • mapperElement(root.evalNode("mappers"));
  • 通过文件地址解析,具体查看下个小标题

    • mapperParser.parse();
public class XMLConfigBuilder extends BaseBuilder {

    // 构建XMLConfig文件对象,在构造方法中调用 parser 方法解析
  private XMLConfigBuilder(Class<? extends Configuration> configClass, XPathParser parser, String environment,
      Properties props) {
    super(newConfig(configClass));
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
      // 在解析配置文件时,判断是否已解析
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

  public Configuration parse() {
      // 在 XMLConfigBuilder 方法赋值,也就是在构建XMLConfigBuilder对象时
      // SqlSessionFactoryBuilder类中的XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
      // parser.evalNode("/configuration") : 返回XNode对象,该对象是"mybatis-config.xml"文件的全部内容
    parseConfiguration(parser.evalNode("/configuration"));
      // 返回 configuration 对象,此对象包含了所有的配置信息如下:
      	// "mybatis-config.xml"
        // 我们业务接口对应的mapper.xml
        // Statement保持的正删改查信息
        // MapperRegistry中保存的namespace的值
    return configuration;
  }

    // 读取"mybatis-config.xml"文件中的配置内容
  private void parseConfiguration(XNode root) {
    try {
        // 先读取properties相关的属性
      // issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
        // 读取settings相关的属性
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
        // settingsElement(settings):"设置默认的配置属性"
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
        // 解析业务接口对应的Mapper.xml(例如UserMapper.xml)
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }


	// 当配置文件内有些属性是没有配置的时候,设置默认属性值并保存在configuration对象中 
  private void settingsElement(Properties props) {
      // 看到这些代码的时,想必所有的配置文件都是这样的,有配置属性的加载配置属性,没有配置属性的设置默认值
    configuration
        .setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
    configuration.setAutoMappingUnknownColumnBehavior(
        AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
    configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
    configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
    configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
    configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
    configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
    configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
    configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
    configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
    configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
    configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
    configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
    configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
    configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
    configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
    configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
    configuration.setLazyLoadTriggerMethods(
        stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
    configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
    configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
    configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
    configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
    configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
    configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
    configuration.setLogPrefix(props.getProperty("logPrefix"));
    configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
    configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
    configuration.setArgNameBasedConstructorAutoMapping(
        booleanValueOf(props.getProperty("argNameBasedConstructorAutoMapping"), false));
    configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
    configuration.setNullableOnForEach(booleanValueOf(props.getProperty("nullableOnForEach"), false));
  }


  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
       // 获取子节点,遍历 
      for (XNode child : parent.getChildren()) {
          // 判断"mybatis-config.xmml"中引入的方式,本文用的是 resource 标签
          //    <mappers>
          //		<mapper resource="mapper/UserMapper.xml"/>
          //		<mapper resource="mapper/DeptMapper.xml"/>
    	  //	</mappers>
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
            // 以 resource 属性引入的方式
          String resource = child.getStringAttribute("resource");
             // 以 url 属性引入的方式
          String url = child.getStringAttribute("url");
             // 以 class 属性引入的方式
          String mapperClass = child.getStringAttribute("class");
            // 判断引入方式,如果以 resource 不等于 null 并且 url 等于 null 并且 class 等于 null
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
              XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,
                  configuration.getSqlFragments());
                  // 调用 mapperParser.parse();
              mapperParser.parse();
            }
              // 判断引入方式,如果以 resource 等于 null 并且 url 不等于 null 并且 class 等于 null
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            try (InputStream inputStream = Resources.getUrlAsStream(url)) {
              XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url,
                  configuration.getSqlFragments());
                // 调用 mapperParser.parse(); 具体逻辑查看下面标题 mapperParser.parse()
              mapperParser.parse();
            }
              // 判断引入方式,如果以 resource 等于 null 并且 url 等于 null 并且 class 不等于 null
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException(
                "A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }
}

mapperParser.parse()
  • 解析业务接口Mapper对应的xml文件
public class XMLMapperBuilder extends BaseBuilder {

    // 解析业务接口Mapper对应的xml文件,由于是for循序调用,每次解析一个xml文件
  public void parse() {
      // 判断资源是否已被加载,返回false,加上!取反
    if (!configuration.isResourceLoaded(resource)) {
        // 获取xml文件中的 Mapper 标签
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
        
        // 看完标题上面configurationElement解析mapper到为buildParameterMapping的方法再来看此方法
        // 添加本地资源和将namespace的值添加到 MapperRegistry 对象中
      bindMapperForNamespace();
    }
       // 以下三个方法笔者debug时没有参数,看不到执行处理过程
    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

  private void configurationElement(XNode context) {
    try {
        // 获取 mapper 标签
      String namespace = context.getStringAttribute("namespace");
        // 判断是否为 null
      if (namespace == null || namespace.isEmpty()) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
        
        // 设置当前命名空间
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
        // 获取 parameterMap 标签
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
        // 获取 resultMap 标签
      resultMapElements(context.evalNodes("/mapper/resultMap"));
        // 获取 SQL 片段
      sqlElement(context.evalNodes("/mapper/sql"));
        // 解析 select|insert|update|delete 标签
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

    // 解析 select|insert|update|delete 标签代码实现
  private void buildStatementFromContext(List<XNode> list) {
      // 如果 databaseId 不等于 null,
      // databaseId 是多数据源标识
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
      // 继续调用 buildStatementFromContext方法
    buildStatementFromContext(list, null);
  }

    // 解析 select|insert|update|delete 标签代码实现
  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
      // 遍历所有 select|insert|update|delete 标签,每个标签都是一个单独的 context 对象
    for (XNode context : list) {
        // 构建解析 select|insert|update|delete 标签的 XMLStatementBuilder 对象
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context,
          requiredDatabaseId);
      try {
          // 这才是真正的解析业务接口Mapper对应的xml文件代码 具体逻辑查看下级标题 parseStatementNode()
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

  private void bindMapperForNamespace() {
      // 获取 namespace 标签的值
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        // ignore, bound type is not required
      }
      if (boundType != null && !configuration.hasMapper(boundType)) {
        // Spring may not know the real resource name so we set a flag
        // to prevent loading again this resource from the mapper interface
        // look at MapperAnnotationBuilder#loadXmlResource
          // 这行代码有趣,看注释说: spring可能不知道真实的资源名称,因此我们设置一个标志来防止从映射器接口再次加载该资源.
        configuration.addLoadedResource("namespace:" + namespace);
          // 将namespace的值添加到 MapperRegistry 对象中,和Statement对象包含所有的增删改查一样,MapperRegistry包含这所有的mapper.xml文件地址
          // 在MapperRegistry对象中有两个属性: 
          // 1. private final Configuration config;
		 // 2. private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
        configuration.addMapper(boundType);
      }
    }
  }

}
parseStatementNode()
  • 解析 mapper.xml 文件中的 select | insert | update | delete 标签(
public class XMLStatementBuilder extends BaseBuilder {

    // 解析 select|insert|update|delete 标签代码实现
  public void parseStatementNode() {
      // 获取select|insert|update|delete标签内的属性ID ,以下只写属性,不在写select|insert|update|delete标签内
    String id = context.getStringAttribute("id");
      // 获取 databaseId 的属性值
    String databaseId = context.getStringAttribute("databaseId");
	 // 判断当前数据库ID是否和标签内的databaseId一致
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
	
      // 获取标签名称.如 select|insert|update|delete 标签
    String nodeName = context.getNode().getNodeName();
      // 转换成枚举对象字段属性
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
      // 判断是否是 select 标签,以下几乎都是对 select|insert|update|delete 标签内的属性进行判断,就不写注释了
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
      
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);

    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    // Parse selectKey after includes and remove them.
    processSelectKeyNodes(id, parameterTypeClass, langDriver);

    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
              ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }

    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    StatementType statementType = StatementType
        .valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String resultType = context.getStringAttribute("resultType");
    Class<?> resultTypeClass = resolveClass(resultType);
    String resultMap = context.getStringAttribute("resultMap");
    if (resultTypeClass == null && resultMap == null) {
      resultTypeClass = MapperAnnotationBuilder.getMethodReturnType(builderAssistant.getCurrentNamespace(), id);
    }
    String resultSetType = context.getStringAttribute("resultSetType");
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    if (resultSetTypeEnum == null) {
      resultSetTypeEnum = configuration.getDefaultResultSetType();
    }
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    String resultSets = context.getStringAttribute("resultSets");
    boolean dirtySelect = context.getBooleanAttribute("affectData", Boolean.FALSE);

      // 把 mapper.xml 中的每个标签和标签中的属性解析并且添加到 MappedStatement 中 查看下面标题 addMappedStatement
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,
        parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets, dirtySelect);
  }

}
buildParameterMapping
  • 把mapper.xml标签中解析完的属性进行校验并且构建 MappedStatement 对象
    • 换而言之,每个 MappedStatement 对象就是 select | insert | update | delete 标签的所有内容
public class MapperBuilderAssistant extends BaseBuilder {


    // XMLStatementBuilder类中调用的 addMappedStatement 方法
  public MappedStatement addMappedStatement(String id, SqlSource sqlSource, StatementType statementType,
      SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType,
      String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache,
      boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId,
      LanguageDriver lang, String resultSets, boolean dirtySelect) {

    if (unresolvedCacheRef) {
      throw new IncompleteElementException("Cache-ref not yet resolved");
    }

      // 命名规范校验
    id = applyCurrentNamespace(id, false);

      // 构建 MappedStatement 对象
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType)
        .keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang)
        .resultOrdered(resultOrdered).resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType)
        .flushCacheRequired(flushCache).useCache(useCache).cache(currentCache).dirtySelect(dirtySelect);

      // 用于获取与给定映射语句相关的参数映射信息
    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
      statementBuilder.parameterMap(statementParameterMap);
    }

    MappedStatement statement = statementBuilder.build();
      // configuration 中定义一个 mappedStatements 的 map 集合,将 Statement 添加到 map 集合中.
      // 在这个for循环中,至此是不是可以推测项目类中所有的增删改查都会存在 mappedStatements 的 map 集合中?   
    configuration.addMappedStatement(statement);
    return statement;
  }

}

sqlSessionFactory.openSession(true)
  • 断点打在 SqlSession sqlSession = sqlSessionFactory.openSession(true),进入openSession方法

  • 获取数据库信息构建 TransactionFactory

    • final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      
  • 通过数据库配置信息,事物隔离级别,是否自动提交构建 Transaction

    • Transaction tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      
  • 通过Transaction类和枚举对象 ExecutorType(默认SIMPLE) 构建 Executor

  • final Executor executor = configuration.newExecutor(tx, execType);
    
  • 使用 configuration && executor && autoCommit 构建 DefaultSqlSession 类并且返回 DefaultSqlSession

    • return new DefaultSqlSession(configuration, executor, autoCommit);
      
// 看源码和SqlSessionFactoryBuilder类一样,有多种构造方法
public class DefaultSqlSessionFactory implements SqlSessionFactory {
    
     /*
        configuration.getDefaultExecutorType(): 获取默认的ExecutorType-> SIMPLE,此枚举有三个字段,SIMPLE | REUSE | BATCH
           SIMPLE: 在SIMPLE执行器下,每次执行SQL语句时都会创建一个新的数据库连接,执行完毕后立即关闭连接。这种执行器类型适用于简单的查询和非事务性操作,但可能在频繁执行SQL语句的情况下引入较大的开销。
           REUSE: MyBatis会重复使用现有的数据库连接,而不是每次都创建新的连接。这可以显著降低连接创建和销毁的开销,适用于频繁执行多个SQL语句的情况,例如事务中的多个查询。
           BATCH: BATCH 执行器类型通常适用于批量操作,MyBatis 会收集多个 SQL 语句,并在执行时一次性提交它们,但不适用于单个查询操作。
       null: 事物隔离级别
       autoCommit: 是否设置自动提交 false | true 
         */
  @Override
  public SqlSession openSession(boolean autoCommit) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
  }

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,
      boolean autoCommit) {
    Transaction tx = null;
    try {
        // 获取"mybatis-config.xml"中的数据库配置
      final Environment environment = configuration.getEnvironment();
        // 通过数据库配置获取 TransactionFactory 类
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        // 通过数据库配置信息,事物隔离级别,是否自动提交构建 Transaction 类
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        /*
        通过Transaction类和枚举对象 ExecutorType(默认SIMPLE) 构建 Executor类,如果开启了二级缓存那么会创建一个缓存执行器,在执行增删改	    查方法时会优先查询缓存,详细内容看 newExecutor 标题
        */
      final Executor executor = configuration.newExecutor(tx, execType);
        // 使用 configuration && executor&& autoCommit 构建 DefaultSqlSession 类,DefaultSqlSession 实现 SqlSession 接口
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}
newExecutor
  • 根据 ExecutorType 类型构建 Executor 类,如果开启二级缓存,则构建 CachingExecutor 对象
public class Configuration {

    //创建 Executor 类
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
      // 三元表达式,判断 ExecutorType 的类型
    executorType = executorType == null ? defaultExecutorType : executorType;
    Executor executor;
      // 根据 ExecutorType 类型构建 Executor 类
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
      // 是否开启二级缓存,如果开启则构建 CachingExecutor 对象
    if (cacheEnabled) {
        // 重点 查看下面标题 CachingExecutor 
      executor = new CachingExecutor(executor);
    }
      // 将 executor 添加到拦截链,使用所有拦截器包装 Executor 类,mybatis也有一个拦截器可以实现
    return (Executor) interceptorChain.pluginAll(executor);
  }
}
CachingExecutor
public class CachingExecutor implements Executor {

  private final Executor delegate;
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();

    // 在 CachingExecutor 的构造方法中传入了 Executor类,只不过在 Executor 类中的 query 方法增加了缓存处理
  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
  }
 
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)
      throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
      // 创建缓存索引
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
}

sqlSession.getMapper(UserMapper.class)
  • 调用 DefaultSqlSession 类中的 getMapper 方法
public class DefaultSqlSession implements SqlSession {

  private final Configuration configuration;
  private final Executor executor;

  private final boolean autoCommit;
  private boolean dirty;
  private List<Cursor<?>> cursorList;

    // 调用 Configuration.getMapper 方法
  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }
}

// 这里不是内部类,而是笔者懒,直接找到 Configuration 类中的 getMapper 方法粘贴到这里
// 调用 mapperRegistry.getMapper方法 
public class Configuration {

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      // 查看下面标题 mapperRegistry.getMapper
    return mapperRegistry.getMapper(type, sqlSession);
  }
}



mapperRegistry.getMapper
public class MapperRegistry {

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        // 获取 MapperProxyFactory 类
      final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
      if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
      }
      try {
          // 返回一个MapperProxy代理类
        return mapperProxyFactory.newInstance(sqlSession);
      } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
      }
    }
}

public class MapperProxyFactory<T> {
    
  public T newInstance(SqlSession sqlSession) {\
      // 使用 sqlSession , mapperInterface , methodCache 构建 MapperProxy 类
      // sqlSession: 包configuration,Executor,是否自动提交
      // mapperInterface: 系统业务接口.如UserMap.java
      // methodCache: 方法缓存
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
      // MapperProxy对象实现了 InvocationHandler 接口,此接口是JavaJDK动态代理一起使用
    return newInstance(mapperProxy);
  }
}



mapper.findById(2)
public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -4724728412955527868L;
  private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
      | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
  private static final Constructor<Lookup> lookupConstructor;
  private static final Method privateLookupInMethod;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache;

    // 构造方法,看源码记得要看下构造方法
  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  static {
    Method privateLookupIn;
    try {
      privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
    } catch (NoSuchMethodException e) {
      privateLookupIn = null;
    }
    privateLookupInMethod = privateLookupIn;

    Constructor<Lookup> lookup = null;
    if (privateLookupInMethod == null) {
      // JDK 1.8
      try {
        lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
        lookup.setAccessible(true);
      } catch (NoSuchMethodException e) {
        throw new IllegalStateException(
            "There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
            e);
      } catch (Exception e) {
        lookup = null;
      }
    }
    lookupConstructor = lookup;
  }

    // proxy: 代理类
    // method: 执行的方法
    // args: SQL参数
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        // 判断method.getDeclaringClass()是不是Object类中的方法,如hashCode和equals和toString
        // 因为method.getDeclaringClass() 是代理类 ,Object是所有类的父类!
      if (Object.class.equals(method.getDeclaringClass())) {
          // 如果是就直接执行Object方法
        return method.invoke(this, args);
      }
        // 调用内部接口的invoke方法
      return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
    // 定义一个内部接口
  interface MapperMethodInvoker {
    Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
  }

    // 内部接口实现类
  private static class PlainMethodInvoker implements MapperMethodInvoker {
    private final MapperMethod mapperMethod;

      // 内部类构造方法,虽然定义了一个新的内部类,但看构造方法还是使用的是 MapperMethod 对象
    public PlainMethodInvoker(MapperMethod mapperMethod) {
      this.mapperMethod = mapperMethod;
    }
	    
      //  再次调用 MapperMethod 的 invoke 方法.具体流程查看 mapperMethod.execute 标题
    @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
      return mapperMethod.execute(sqlSession, args);
    }
  }
}
mapperMethod.execute
public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }
  
    // MapperMethod 的 invoke 方法
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
      // 判断增删改查标签
    switch (command.getType()) {
            // 新增方法
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
          // 更新方法
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
            // 删除方法
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
            // 查询方法
      case SELECT:
            // 如果方法没有返回值
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
           // 如果方法返回多条数据
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
            // 如果方法返回Map集合
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
            // 如果方法返回游标
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
            // 以上结果都不满足
        } else {
            // 对参数SQL参数进行解析,具体查看 method.convertArgsToSqlCommandParam(args) 标题
          Object param = method.convertArgsToSqlCommandParam(args);
            // command.getName(): 获取增删改查标签的id值
            // param: 包含参数的 map 集合
            // sqlSession.selectOne: 调用 selectOne 方法,具体内容请查看 sqlSession.selectOne
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName()
          + "' attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }
}
method.convertArgsToSqlCommandParam(args)
  • UserMapper.java 传进来的参数进行解析
public class ParamNameResolver {

    // 定义参数名前缀
  public static final String GENERIC_NAME_PREFIX = "param";

  private final boolean useActualParamName;

  private final SortedMap<Integer, String> names;

  private boolean hasParamAnnotation;

    // 参数名称解析器
  public ParamNameResolver(Configuration config, Method method) {
      // 获取参数使用的实际名称
    this.useActualParamName = config.isUseActualParamName();
      // 获取参数类型
    final Class<?>[] paramTypes = method.getParameterTypes();
      // 获取使用 @Parame 注解的参数,这是一个二维数组,第一个数组装的是注解的索引值,第二个装的是索引值对应的参数相关信息
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
      // 创建一个 map ,用来存储后续解析完的参数   
    final SortedMap<Integer, String> map = new TreeMap<>();
      // 获取数组长度
    int paramCount = paramAnnotations.length;
    // get names from @Param annotations (从@Param注释中获取名称)
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      if (isSpecialParameter(paramTypes[paramIndex])) {
        // skip special parameters
        continue;
      }
      String name = null;
        // 变量二维数组
      for (Annotation annotation : paramAnnotations[paramIndex]) {
          // 如果注解中包含 Param
        if (annotation instanceof Param) {
            // 设置为ture
          hasParamAnnotation = true;
            // 将注解的值赋值给name, org.apache.ibatis.annotations.Param("id")
          name = ((Param) annotation).value();
          break;
        }
      }
      if (name == null) {
        // @Param was not specified.
        if (useActualParamName) {
          name = getActualParamName(method, paramIndex);
        }
        if (name == null) {
          // use the parameter index as the name ("0", "1", ...)
          // gcode issue #71
          name = String.valueOf(map.size());
        }
      }
        // 将注解的索引和注解的值存入map
      map.put(paramIndex, name);
    }
      // 存入成员变量中,方便其他方法使用
    names = Collections.unmodifiableSortedMap(map);
  }


  /**
   * <p>
   * A single non-special parameter is returned without a name. Multiple parameters are named using the naming rule. In
   * addition to the default names, this method also adds the generic names (param1, param2, ...).
   * </p>
   * 返回一个没有名称的非特殊参数。多个参数使用命名规则命名。除了默认名称之外,此方法还添加通用名称(param1、param2、...)
   */
    // 参数解析 对照源码上的注释,加上类中定义的参数前缀,在使用Mybatis中可以按照参数的前后顺序也可以用param1、param2、..进行取值
  public Object getNamedParams(Object[] args) {
      // 获取参数数量
    final int paramCount = names.size();
      // 非空判断
    if (args == null || paramCount == 0) {
      return null;
    }
      // 如果参数是单个,直接返回
    if (!hasParamAnnotation && paramCount == 1) {
      Object value = args[names.firstKey()];
      return wrapToMapIfCollection(value, useActualParamName ? names.get(names.firstKey()) : null);
        // 如果参数是多个,封装成一个map
    } else {
        // 定义参数封装集合
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
        // 遍历参数集合,至于这个 names.entrySet() 是哪里来的数据,请debug此类中的 ParamNameResolver 方法
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        param.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)
        // 添加生成的参数名称.如 param1, param2, ...
        final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
        // ensure not to overwrite parameter named with @Param (确保不覆盖以@Param命名的参数)
        // 如果 param 集合中不包含 genericParamName 则添加到 param集合中
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
        // 返回包含参数的 map 集合
      return param;
    }
  }
}

sqlSession.selectOne
public class DefaultSqlSession implements SqlSession {

  private final Configuration configuration;
  private final Executor executor;

  private final boolean autoCommit;
  private boolean dirty;
  private List<Cursor<?>> cursorList;

  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

  public DefaultSqlSession(Configuration configuration, Executor executor) {
    this(configuration, executor, false);
  }

  @Override
  public <T> T selectOne(String statement) {
    return this.selectOne(statement, null);
  }
 
    
   // 调用 selectOne 方法后续
    // statement: 增删改查标签的id值
    // parameter: 解析完后的参数集合
  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // 其实在Mybatis中执行的selectOne访问,本质上还是调用了selectList方法,只不过在返回值上进行了处理\
      // 继续往下查看 selectList 方法内部逻辑
    List<T> list = this.selectList(statement, parameter);
      // 如果结果是单个,就取第一个,笔者看到这行代码时愣了一下,这才回想起索引是从0开始的,size并不是~
    if (list.size() == 1) {
      return list.get(0);
    }
      // 如果结果是多个,直接抛出异常,经典报错!相信大家都不会陌生
    if (list.size() > 1) {
      throw new TooManyResultsException(
          "Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }

    // selectList 方法
  @Override
  public <E> List<E> selectList(String statement) {
       // 继续向下调用
    return this.selectList(statement, null);
  }

    // 继续向下调用
  @Override
  public <E> List<E> selectList(String statement, Object parameter) {
      // 继续向下调用
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

     // 继续向下调用
  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
       // 继续向下调用
    return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);
  }

    // selectList 处理过程
  private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
        // 在标题 SqlSessionFactoryBuilder 的下文有提到过,
        // MappedStatement是configuration中的一个集合,此集合存储着所有的增删改查标签的内容,
        // 以属性ID的值为key,增删改查标签的所有内容为value
        // 通过属性ID的key获取对应的语句标签内容
        // 在getMappedStatement 方法中还有一个比较有意思的方法(buildAllStatements),看方法名是构建所有的Statement,
        // 但是笔者在debug的时候没有数据,无法进入判断语句,待后续在了解
      MappedStatement ms = configuration.getMappedStatement(statement);
        // 判断是否是脏读,数据库的四种隔离级别,读未提交(脏读),读已提交,.可重复读和串行化
      dirty |= ms.isDirtySelect();
        // wrapCollection(parameter) 标题查看,重点
        // executor.query 标题查看
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

 
}

wrapCollection(parameter)
  • 在上面的 method.convertArgsToSqlCommandParam(args) 标题中是对多个参数的解析
  • 而在 wrapCollection(parameter) 中是对集合类型的数据进行解析
public class ParamNameResolver {

  public static final String GENERIC_NAME_PREFIX = "param";

  private final boolean useActualParamName;

  /**
   * Wrap to a {@link ParamMap} if object is {@link Collection} or array.
   *
   * @param object
   *          a parameter object
   * @param actualParamName
   *          an actual parameter name (If specify a name, set an object to {@link ParamMap} with specified name)
   *
   * @return a {@link ParamMap}
   *
   * @since 3.5.5
   */
    // 对集合类型的参数进行解析
  public static Object wrapToMapIfCollection(Object object, String actualParamName) {
      // 如果是集合
    if (object instanceof Collection) {
        // 创建map集合,存储value
      ParamMap<Object> map = new ParamMap<>();
        // 以字符串"collection" 为key和value为Mapper.xml中的入参存储到map
      map.put("collection", object);
        // 如果List
      if (object instanceof List) {
           // 以字符串"list" 为key和value为Mapper.xml中的入参存储到map
        map.put("list", object);
      }
        // 如果有使用 @param 注解给定参数名称,则添加到map中
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
         // 返回该map
      return map;
    }
      // 如果参数 != nll 并且是数组
    if (object != null && object.getClass().isArray()) {
      ParamMap<Object> map = new ParamMap<>();
         // 以字符串"array" 为key和value为Mapper.xml中的入参存储到map
      map.put("array", object);
            // 如果有使用 @param 注解给定参数名称,则添加到map中
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
        // 返回该map
      return map;
    }
    return object;
  }
}
executor.query
public class CachingExecutor implements Executor {

  private final Executor delegate;
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();

  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
  }

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)
      throws SQLException {
      // 获取 BoundSql 代表SQL语句的详细信息:SQL语句,参数,以及参数在SQL语句中的映射关系
      // setParameters: 在 PreparedStatement 类中的 setParameters 方法中完成参数对#{}的替换
    BoundSql boundSql = ms.getBoundSql(parameterObject);
      // 创建缓存的key: 当前的hashcode,当前的检查码,增删改查的方法名,参数的hashcode,执行的SQL语句,传入的参数值和当前环境信息
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
      // 调用下面的 query 方法(在前文有说过,CachingExecutor 的构造方法传入 Executor 对象,
      // delegate.setExecutorWrapper(this)跟踪下去发现在 BaseExecutor 中 声明了 Executor 类就是 this)
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

    //  query 方法
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler,
      CacheKey key, BoundSql boundSql) throws SQLException {
      // 获取二级缓存
    Cache cache = ms.getCache();
      // 如果缓存 != null : 本次 debug 没有开启二级缓存
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
      // 调用 BaseExecutor 类中的 query 方法 请查看标题: BaseExecutor.query
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
}
BaseExecutor.query
public abstract class BaseExecutor implements Executor {

  private static final Log log = LogFactory.getLog(BaseExecutor.class);

  protected Transaction transaction;
  protected Executor wrapper;

  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  protected PerpetualCache localCache;
  protected PerpetualCache localOutputParameterCache;
  protected Configuration configuration;

  protected int queryStack;
  private boolean closed;

  protected BaseExecutor(Configuration configuration, Transaction transaction) {
    this.transaction = transaction;
    this.deferredLoads = new ConcurrentLinkedQueue<>();
    this.localCache = new PerpetualCache("LocalCache");
    this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
    this.closed = false;
    this.configuration = configuration;
    this.wrapper = this;
  }

  
    // 
  @SuppressWarnings("unchecked")
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
      CacheKey key, BoundSql boundSql) throws SQLException {
      
      // ms.getResource(): UserMapper.xml的相对路径
      // ms.getId(): insert | select | update | delete 标签中的ID属性
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
      // 判断是否清除本地缓存
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
        // 如果 resultHandler == null 则用缓存的key去查缓存,否则 = null 
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
        // 如果一级缓存中有
      if (list != null) {
          // 将缓存中的数处理为返回结果
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
          // 如果一级缓存中没有,则去数据库中查询
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

  /**
   * Apply a transaction timeout.
   *
   * @param statement
   *          a current statement
   *
   * @throws SQLException
   *           if a database access error occurs, this method is called on a closed <code>Statement</code>
   *
   * @since 3.4.0
   *
   * @see StatementUtil#applyTransactionTimeout(Statement, Integer, Integer)
   */
  protected void applyTransactionTimeout(Statement statement) throws SQLException {
    StatementUtil.applyTransactionTimeout(statement, statement.getQueryTimeout(), transaction.getTimeout());
  }

    // 处理一级缓存的输出参数
  private void handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter,
      BoundSql boundSql) {
    if (ms.getStatementType() == StatementType.CALLABLE) {
      final Object cachedParameter = localOutputParameterCache.getObject(key);
      if (cachedParameter != null && parameter != null) {
        final MetaObject metaCachedParameter = configuration.newMetaObject(cachedParameter);
        final MetaObject metaParameter = configuration.newMetaObject(parameter);
        for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
          if (parameterMapping.getMode() != ParameterMode.IN) {
            final String parameterName = parameterMapping.getProperty();
            final Object cachedValue = metaCachedParameter.getValue(parameterName);
            metaParameter.setValue(parameterName, cachedValue);
          }
        }
      }
    }
  }

    // 从数据库去查询
  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds,
      ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
      // 将key添加到缓存中. EXECUTION_PLACEHOLDER: 占位符,没有什么实际意义
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
        // 前文有说到,使用Executor的类型是 SIMPLE ,so doQuery 会被 SimpleExecutor 调用,查看下面标题  SimpleExecutor.doQuery
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
        // 删除key
      localCache.removeObject(key);
    }
      // 将查询结果存入缓存
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }
}
SimpleExecutor.doQuery
public class SimpleExecutor extends BaseExecutor {

  public SimpleExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }
    // 从数据库查询数据
    // ms: 当前查询标签的详细信息
    // parameter: sql参数
    // rowBounds: Mybatis封装的逻辑分页,不好用而没什么人使用
    // resultHandler: 暂时没有使用到,看命名就知道是处理返回结果的
    // boundSql: SQL语句的详细信息:SQL语句,参数,以及参数在SQL语句中的映射关系
  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
      BoundSql boundSql) throws SQLException {
      // 声明 Statement 对象,此对象也是原生JDBC的 Statement
    Statement stmt = null;
    try {
        // 获取"mybatis-config.xml"配置信息
      Configuration configuration = ms.getConfiguration();
        // 重点 configuration.newStatementHandler构建 StatementHandler 对象,具体逻辑请查看 newStatementHandler 标题
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler,
          boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
        // 执行查询语句
        11
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
  
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
      // 声明 Statement 对象
    Statement stmt;
      // 获取数据库链接
    Connection connection = getConnection(statementLog);
      // 解析 connection 类, 查看下面标题 prepare
    stmt = handler.prepare(connection, transaction.getTimeout());
      // 重点:参数预编译,查看下面标题 parameterize
    handler.parameterize(stmt);
    return stmt;
 }   
}
newStatementHandler
public class Configuration {

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement,
      Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
      // 构建 StatementHandler 对象,实际返回的是 PreparedStatementHandler对象,具体逻辑查看下面标题  RoutingStatementHandler
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject,
        rowBounds, resultHandler, boundSql);
      // 将 statementHandler 添加到拦截链,使用所有拦截器包装 statementHandler 类,在Executor中有同样的操作
    return (StatementHandler) interceptorChain.pluginAll(statementHandler);
  }
}
RoutingStatementHandler
  • 用于构建原生 JDBC 中的 Statement 对象
public class RoutingStatementHandler implements StatementHandler {

  private final StatementHandler delegate;

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,
      ResultHandler resultHandler, BoundSql boundSql) {
      /*
        - statementType 有三个属性,在默认值是 **PREPARED**,在java.sql包下,这三个属性都是接口形式。
          - STATEMENT
            - 在 Java 中 Statement 继承自 Wrapper ,提供了执行语句和获取结果的基本方法。
            - 普通的不带参的查询SQL,支持批量更新,批量删除。
            - Statement 每次执行sql语句,数据库都要执行sql语句的编译 。
            - 最好用于仅执行一次查询并返回结果的情形,效率高于 PreparedStatement。 
          - PREPARED 
            - 在 Java 中 PreparedStatement 继承自 Statement,添加了处理 IN 参数的方法。
            - 可变参数的SQL,编译一次,执行多次,效率高。
            - 安全性好,有效防止Sql注入等问题。
            - 支持批量更新,批量删除。
            - PreparedStatement 是预编译的,使用 PreparedStatement 有几个好处:
              - 在执行可变参数的一条SQL时,PreparedStatement 比 Statement 的效率高,因为DBMS预编译一条SQL当然会比多次编译一条SQL的效率要高。
              - 安全性好,有效防止Sql注入等问题。
              - 对于多次重复执行的语句,使用 PreparedStatement 效率会更高一点,并且在这种情况下也比较适合使用batch;
              - 代码的可读性和可维护性。 
          - CALLABLE
            - 在 Java 中 CallableStatement 继承自 PreparedStatement ,添加了处理 OUT 参数的方法。
            - 继承自 PreparedStatement ,支持带参数的 SQL 操作;
            - 支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持;
         */
    // 判断 select 标签中的 StatementType 属性
    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
            // 返回一个PreparedStatementHandler
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }
}
prepare
  • 参数预编译前的准备工作中
  • 在调用 stmt = handler.prepare(connection, transaction.getTimeout()) 方法时,handler对象是 RoutingStatementHandler,而RoutingStatementHandler 类的中定义了 private final StatementHandler delegate
public class RoutingStatementHandler implements StatementHandler {

  private final StatementHandler delegate;
    // 构造方法
  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,
      ResultHandler resultHandler, BoundSql boundSql) {

    // 此处调用编译前的准备 -> stmt = handler.prepare(connection, transaction.getTimeout())
    // 这里 delegate 类型则是 StatementHandler
  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    return delegate.prepare(connection, transactionTimeout);
  }

    // 同上,都是调用了 StatementHandler 的方法进行处理
  @Override
  public void parameterize(Statement statement) throws SQLException {
    delegate.parameterize(statement);
  }
  • StatementHandler 是一个接口,它的另一个实现则是 BaseStatementHandler,该实现类中有对 prepare 方法的具体实现
BaseStatementHandler implements StatementHandler {

  protected final Configuration configuration;
  protected final ObjectFactory objectFactory;
  protected final TypeHandlerRegistry typeHandlerRegistry;
  protected final ResultSetHandler resultSetHandler;
  protected final ParameterHandler parameterHandler;

  protected final Executor executor;
  protected final MappedStatement mappedStatement;
  protected final RowBounds rowBounds;

  protected BoundSql boundSql;

  protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject,
      RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    this.configuration = mappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;

    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;
      // 以下两个对象,创建的过程同等于 newStatementHandler 标题中的内容
    // 构建 parameterHandler 对象,parameterHandler实现类中DefaultParameterHandler的setParameters方法是用于 参数 于 #{} 的替换,
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    // 构建 resultSetHandler 对象,用于处理返回结果
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler,
        resultHandler, boundSql);
  }
    
  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
        //  使用 connection 初始化 Statement 类
      statement = instantiateStatement(connection);
        // 设置语句超时实际
      setStatementTimeout(statement, transactionTimeout);
        //  FetchSize: 在解析内有设置此属性的值,标明一次性获取数据的条数,查看下面标题 parameterize    
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }
parameterize
  • 使用 ParameterHandler 处理参数
public class DefaultParameterHandler implements ParameterHandler {

  private final TypeHandlerRegistry typeHandlerRegistry;

  private final MappedStatement mappedStatement;
  private final Object parameterObject;
  private final BoundSql boundSql;
  private final Configuration configuration;

  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
  }

  @Override
  public Object getParameterObject() {
    return parameterObject;
  }

  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
            // 调用 TypeHandler 设置预编译参数
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }
}
handler.query
  • query方法调用 RoutingStatementHandlerquery 方法,和上述一样,而 delegate 的类型是 StatementHandler ,StatementHandler 是一个接口,具体实现在 PreparedStatementHandler 类中(光标选中 StatementHandler 。 ctrl + H 查看类关系图)

    • RoutingStatementHandler
    public class RoutingStatementHandler implements StatementHandler {
      private final StatementHandler delegate;
      @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        return delegate.query(statement, resultHandler);
      }
    
    • 具体实现 query 方法
public class PreparedStatementHandler extends BaseStatementHandler {

  public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter,
      RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
      // 查询结果处理 查看 handleResultSets(ps) 标题
    return resultSetHandler.handleResultSets(ps);
  }
}
handleResultSets(ps)
public class DefaultResultSetHandler implements ResultSetHandler {

  @Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }
    
  private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
    ResultSet rs = stmt.getResultSet();
    while (rs == null) {
      // move forward to get the first resultset in case the driver
      // doesn't return the resultset as the first result (HSQLDB 2.1)
      if (stmt.getMoreResults()) {
        rs = stmt.getResultSet();
      } else if (stmt.getUpdateCount() == -1) {
        // no more results. Must be no resultset
        break;
      }
    }
    return rs != null ? new ResultSetWrapper(rs, configuration) : null;
  }  
    
  private void validateResultMapsCount(ResultSetWrapper rsw, int resultMapCount) {
    if (rsw != null && resultMapCount < 1) {
      throw new ExecutorException(
          "A query was run and no Result Maps were found for the Mapped Statement '" + mappedStatement.getId()
              + "'. 'resultType' or 'resultMap' must be specified when there is no corresponding method.");
    }
  }

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

尾声

  • 学习源码可以帮你更好的使用该框架,最重要的是可以学习里面的编程思想(编程思想这个说法可能会有点抽象,说白了就是编程方式)
文字版总结
  • 学习源码可以帮你更好的使用该框架,最重要的是可以学习里面的编程思想(编程思想这个说法可能会有点抽象,说白了就是编程方式)

  • 构建 XMLConfigBuilder对象,在构造方法中调用parser 方法解析 mybatis-config.xml文件

    • 解析configuration标签,获取所有配件信息,如果没有配置则设置其默认值(在SpringBoot框架,MybatisProperties类用于所有配置属性)
    • 解析 mybatis-config.xml文件的mappers标签,获取标签下所有的Mapper.xml配置路径,遍历配置路径,通过路径获取Mapper.xml文件,获取Mapper.xml文件下的所有标签(真的是解析了所有标签,但是笔者下面就记录两个重要的标签)
    • 解析namespace的值,添加到 Configuration 类中的MapperRegistry对象中,换而言之MapperRegistry对象存储着项目所有的namespace
    • 解析select|insert|update|delete标签时,每一个MappedStatement对象就是一个select|insert|update|delete标签(如果你的Mapper.xml文件中只有三select标签,Mybatis就会生成三个MappedStatemen对象
  • 其实在SqlSession中包含configuration对象,而configuration对象中包含所有的配置信息

  • 构建SqlSession 对象

    • 通过configuration.getEnvironment()方法获取构建TransactionFactory对象
    • 通过数据库配置信息,事物隔离级别,是否自动提交构建 Transaction
    • 通过Transaction类和枚举对象 ExecutorType(默认SIMPLE) 构建 Executor
    • 使用 configuration && executor && autoCommit 构建 DefaultSqlSession 类并且返回 DefaultSqlSession(DefaultSqlSession实现SqlSession 对象)
  • 获取Mapper.xml代理类对象

    • 调用 DefaultSqlSession 类中的 getMapper 方法
    • DefaultSqlSession 类中,getMapper方法的实现是configuration.getMapper,在configuration.getMapper中又调用``mapperRegistry.getMapper(在前面有写,MapperRegistry对象存储着项目所有namespace`的值)
    • 使用 sqlSessionmapperInterfacemethodCache 构建 MapperProxy
  • 执行Mapper.xml中的方法

    • 判读方法的select|insert|update|delete标签

    • 判断方法是否有返回值

    • 获取使用 @Parame 注解的参数,如有参数则设置key,将SQL参数为value并存入map集合

    • 获取 BoundSql:代表SQL语句的详细信息:SQL语句、参数、以及参数在SQL语句中的映射关系

    • 创建缓存的key: 当前的hashcode、当前的检查码、增删改查的方法名、参数的hashcode、执行的SQL语句,传入的参数值和当前环境信息

    • 获取二级缓存对象,在这里不多叙述,本次debug没有开启二级缓存

    • 用缓存Key查询一级缓存,如果有则处理缓存中的数据作为返回结果,本次debug没有则再去从数据库中查询

    • 使用当前查询标签的详细信息、SQL参数、Mybatis封装的逻辑分页、resultHandlerboundSqlboundSql、构建StatementHandler对象

      • 创建数据库连接对象Connection
      • 解析Connection,使用 Connection初始化Statement类,设置语句超时实际。
        • FetchSize::标明一次性获取数据的条数
    • 使用TypeHandler处理预编译参数,大胆猜测是SQL参数#{}的替换,笔者对这段源码没有debug

    • 使用ResultSetHandler.handleResultSets方法对查询结果的处理

面试版
  • 解析 mybatis-config.xmlMapper.xml文件。获取相关信息存入MapperRegistryMappedStatement,创建configuration对象
  • 构建SqlSession 中通过configuration对象创建TransactionFactoryTransactionExecutor、使用 configuration && executor && autoCommit 构建 DefaultSqlSession 类并且返回
  • 使用 sqlSessionmapperInterfacemethodCache 构建 MapperProxy
  • 执行方法时对方法标签、返回值的判断,创建BoundSql、创建缓存的key,先查二级缓存如果二级缓存没有,再查一级如果一级缓存没有,再查数据库,使用TypeHandler处理预编译参数,再用ResultSetHandler.handleResultSets对查询结果的处理
疑惑
  • configuration 类中有一个 buildAllStatements 方法,此方法在笔者debug时,由于没有参数,进不去判断,看方法名和注释,感觉这个方法对理解源码肯定有帮助。
  • CachingExecutorquery 方法中,由于没有开启二级缓存,查看不到对缓存的处理
posted @ 2023-10-15 01:20  unknown-n2  阅读(15)  评论(0编辑  收藏  举报