MyBatis 源码解析:通过源码深入理解 SQL 的执行过程

向作者提问
某大型互联网电商公司的资深后端工程师,对源码、架构颇有兴趣和研究。深知阅读源码的不易,需花费大量的时间和精力,现将自己阅读源码的方式和收获分享给大家,希望会真正帮到你,谢谢
查看本场Chat

你好,首先感谢你能订阅这篇文章,可能篇幅有点长,还希望你能耐心读下去,相信我不会让你失望的。下面我们来开启 MyBatis 的学习之旅吧。

一、目录

二、前言

2.1 MyBatis 框架图

enter image description here

上图为 MyBatis 的框架图,在这篇文章中将通过源码的方式来重点说明数据处理层中的参数映射,SQL 解析,SQL 执行,结果映射。

2.2 配置使用

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.0</version>
    </dependency>

获取 mapper 并操作数据库代码如下:

    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");  
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().
        build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    LiveCourseMapper mapper = sqlSession.getMapper(LiveCourseMapper.class); 
    List<LiveCourse> liveCourseList = mapper.getLiveCourseList();

三、配置文件加载

配置文件加载最终还是通过 ClassLoader.getResourceAsStream 来加载文件,关键代码如下:

    public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
        InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
        if (in == null) {
          throw new IOException("Could not find resource " + resource);
        }
        return in;
  }

    InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
        for (ClassLoader cl : classLoader) {
          if (null != cl) {

            // try to find the resource as passed
            InputStream returnValue = cl.getResourceAsStream(resource);

            // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
            if (null == returnValue) {
              returnValue = cl.getResourceAsStream("/" + resource);
            }

            if (null != returnValue) {
              return returnValue;
            }
          }
    }
    return null;
  }

四、配置文件解析

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

我们以 SqlSessionFactoryBuilder 为入口,看下 MyBatis 是如何解析配置文件,并创建 SqlSessionFactory 的,SqlSessionFactoryBuilder.build 方法实现如下:

 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
 //解析出configuration对象,并创建SqlSessionFactory
 return build(parser.parse());

重点为解析 configuration 对象,然后根据 configuration 创建 DefualtSqlSessionFactory。

4.1 解析 configuration

  private void parseConfiguration(XNode root) {
    try {
      Properties settings = settingsAsPropertiess(root.evalNode("settings"));
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      loadCustomVfs(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectionFactoryElement(root.evalNode("reflectionFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

通过 XPathParser 解析 configuration 节点下的 properties,settings,typeAliases,plugins,objectFactory,objectWrapperFactory,reflectionFactory,environments,databaseIdProvider,typeHandlers,mappers 这些节点,解析过程大体相同,都是通过 XPathParser 解析相关属性、子节点,然后创建相关对象,并保存到 configuration 对象中。

滕俊杰
老师讲的很不错,大体脉络已经讲清楚了。
微信扫描登录