mybatis 配置初始化过程(1)

开篇

 过去一周忙着上线一个线上服务,没时间阅读源码,幸好服务已经顺利上线,可以抽空开始mybatis的系列了,没错这周开始准备开启mybatis的整个系列,欢迎大家订阅。

 按照我现在粗浅的理解,从mybatis的使用过程来看基本可以分为三大步骤,分别是:

  • 配置加载 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  • 会话建立 SqlSession sqlSession = sqlSessionFactory.openSession();
  • 执行查询 User user = sqlSession.selectOne("userTest.selectUser", 1);

 这篇博文主要讲解下配置加载的过程,由于配置加载涉及很多标签的加载,这篇文章暂时只分析宏观的过程,细节每个标签的加载后面文章再分析,核心需要记住mybatis的核心是 all in configuration

补充一点大家不要把mybatis和spring使用mybatis直接混在一起,因为spring和mybatis之间隔了一层spring-mybatis的实现,我们这边只讲解最纯的mybatis。


解析过程概述

 在mybatis的使用过程中,我们构建一个配置的输入reader并通过SqlSessionFactoryBuilder解析配置生成SqlSessionFactory,我们的解析过程就在创建SqlSessionFactory的过程当中。

public class MyBatisTest {
    public static void main(String[] args) throws Exception {
        String resource = "configuration.xml";
        Reader reader = Resources.getResourceAsReader(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(reader);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = sqlSession.selectOne("userTest.selectUser", 1);
        System.out.println(user.getUsername());
        sqlSession.close();
    }
}


配置解析过程

环境配置xml文件

 mybatis的xml配置当中我认为可以分为两大块,分别是environment和mappers,前者主要是指整个mybatis连接的环境配置,后者表示mybatis写的SQL语句,配置的解析过程其实就是解析这块内容的。
 关于mybatis支持的xml标签可以参考文章中的mybatis官网,相信我看了以后你一定不会后悔的,包括:

  • properties
  • settings
  • typeAliases
  • typeHandlers
  • objectFactory
  • plugins 插件
  • environments 环境
  • databaseIdProvider 数据库厂商标识
  • mappers 映射器
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://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.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost/mybatis?useSSL=false" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
            </dataSource>
        </environment>
    </environments>
    <!-- 映射文件 -->
    <mappers>
        <mapper resource="map/query.xml" />
        <mapper resource="map/insert.xml" />
        <mapper resource="map/update.xml" />
        <mapper resource="map/delete.xml" />
    </mappers>
</configuration>    


环境配置解析源码分析

 在XMLConfigBuilder类当中parseConfiguration负责解析上面的xml文件,基本上每个标签对应的都有一个解析类,这里我们就不展开了,每个标签的解析其实都是一个比较复杂的逻辑。
 我们关注下mapperElement方法,这个方法是解析mappers标签引入的对应的SQL语句的xml文件,通过Resources.getResourceAsStream方法读取xml文件内容,通过mapperParser.parse()方法进行解析

private void parseConfiguration(XNode root) {
    try {
      //分步骤解析
      //issue #117 read properties first
      //1.properties
      propertiesElement(root.evalNode("properties"));
      //2.类型别名
      typeAliasesElement(root.evalNode("typeAliases"));
      //3.插件
      pluginElement(root.evalNode("plugins"));
      //4.对象工厂
      objectFactoryElement(root.evalNode("objectFactory"));
      //5.对象包装工厂
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //6.设置
      settingsElement(root.evalNode("settings"));
      // read it after objectFactory and objectWrapperFactory issue #631
      //7.环境
      environmentsElement(root.evalNode("environments"));
      //8.databaseIdProvider
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //9.类型处理器
      typeHandlerElement(root.evalNode("typeHandlers"));
      //10.映射器
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, 
             resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, 
            url, configuration.getSqlFragments());
            mapperParser.parse();
          } 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.");
          }
        }
      }
    }
  }


SQL解析

SQL的XML格式

 我们通过标签<mapper resource="map/insert.xml" />引入的SQL文件的内容如下,mapper其实包含的标签个数不是特别多,可以给大家感受下:

  • cache – 给定命名空间的缓存配置。
  • cache-ref – 其他命名空间缓存配置的引用。
  • resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
  • parameterMap – 已废弃!老式风格的参数映射。内联参数是首选,这个元素可能在将来被移除,这里不会记录。
  • sql – 可被其他语句引用的可重用语句块。
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.daoInterface.UserDao">
    <resultMap id="UserMap" type="domain.UserEntity">
        <id property="id" column="uid" />
        <result property="username" column="username" />
        <result property="password" column="password"/>
        <result property="address" column="address"/>
        <result property="createTime" column="createTime"/>
        <result property="updateTime" column="updateTime"/>
        <collection property="campanyEntity" resultMap="dao.daoInterface.CampanyDao.CampanyMap" />
    </resultMap>

    <!-- 可以将sql语句独立出来,然后引用 -->
    <sql id="selectMap">
        u.username, u.address ,c.campany_name
    </sql>

    <!-- 根据id查询用户 -->
    <select id="getUserInfo" parameterType="int" resultMap="UserMap">
            SELECT <include refid="selectMap"/>
            FROM  user u left join campany c
            ON u.username = c.username
            WHERE id = #{id}
            ORDER BY id ASC
    </select>
</mapper>


SQL解析源码分析

 在XMLMapperBuilder类中通过parse进行解析,从源码可以看出来从标签/mapper开始解析,一次解析SQL对应的各类标签,针对每个标签的解析都是比较复杂的,这个暂时不展开进行分析。

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }
}

private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      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);
    }
  }


配置解析过程流程图

 核心在于XMLConfiggBuilder解析环境变量和XMLMapperBuilder解析SQL变量,这个图应该还算比较清晰,通过解析后核心就生成了Configuration对象,这个是整个mybatis的核心

mybatis 配置初始化过程(1)
配置解析时序图


参考文章

mybatis官网

上一篇:Windows10更新1903后,chrome内核浏览器打开网页链接错误


下一篇:vim 设置配色方案