Mybatis源码学习之全局配置文件和映射文件解析

news2025/2/12 23:22:02

全局配置文件和映射文件解析

全局配置文件解析

  public static void main(String[] args) throws IOException {

    // 读取配置文件
    InputStream is = Resources.getResourceAsStream("org/apache/ibatis/builder/MapperConfig1.xml");
    // 创建SqlSessionFactory工厂
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = sqlSessionFactoryBuilder.build(is);
    // 使用工厂生产SqlSession对象
    SqlSession session = factory.openSession();
     //使用SqlSession创建Dao接口的代理对象
    AuthorMapper authorMapper = session.getMapper(AuthorMapper.class);
    List<Author> authors = authorMapper.selectAllAuthors();
    //释放资源
    session.close();
    is.close();
  }

下面我们来进行源码分析。

配置文件的解析&创建SqlSessionFactory

配置文件的解析主要涉及到的类如下:XMLConfigBuilder、XPathParser、XPath、XNode,其中XPath、XNode是对
1、build方法内部首先会根据输入流等信息创建XMLConfigBuilder类的实例对象,然后调用XMLConfigBuilder实例的parse方法对配置文件进行解析;这里需要注意的是parse方法最后返回的是一个Configuration对象
在这里插入图片描述

2、parse方法则是调用了XPath对象的evalNode方法对配置文件中的configuration节点进行解析,会把节点内容放在XNode对象中然后返回;
在这里插入图片描述

3、parseConfiguration方法会对configuration节点解析出来的内容再进行解析,会把解析出来的内容放在configuration对象中;实际上配置文件中的内容解析出来后都会存到Configuration中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YjYs8014-1685886063612)(Pasted%20image%2020230223222648.png)]

4、parseConfiguration方法中主要做的事如下:

  • 解析 properties节点
      /**
       * 解析 properties节点
       *     <properties resource="mybatis/db.properties" />
       *     解析到org.apache.ibatis.parsing.XPathParser#variables
       *           org.apache.ibatis.session.Configuration#variables
       */
      // issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
  • 解析settings节点
      /**
       * 解析我们的mybatis-config.xml中的settings节点
       * 具体可以配置哪些属性:http://www.mybatis.org/mybatis-3/zh/configuration.html#settings
       * <settings>
       <setting name="cacheEnabled" value="true"/>
       <setting name="lazyLoadingEnabled" value="true"/>
       <setting name="mapUnderscoreToCamelCase" value="false"/>
       <setting name="localCacheScope" value="SESSION"/>
       <setting name="jdbcTypeForNull" value="OTHER"/>
       ..............
       </settings>
       *
       */
      Properties settings = settingsAsProperties(root.evalNode("settings"));
  • 解析
      /**
       * 基本没有用过该属性
       * VFS含义是虚拟文件系统;主要是通过程序能够方便读取本地文件系统、FTP文件系统等系统中的文件资源。
       Mybatis中提供了VFS这个配置,主要是通过该配置可以加载自定义的虚拟文件系统应用程序
       解析到:org.apache.ibatis.session.Configuration#vfsImpl
       */
      loadCustomVfs(settings);
  • 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
      /**
       * 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
       * SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
       * 解析到org.apache.ibatis.session.Configuration#logImpl
       */
      loadCustomLogImpl(settings);
  • 解析别名
      /**
       * 解析别名
       * <typeAliases>
       <typeAlias alias="Author" type="cn.tulingxueyuan.pojo.Author"/>
       </typeAliases>
       <typeAliases>
       <package name="cn.tulingxueyuan.pojo"/>
       </typeAliases>
       解析到oorg.apache.ibatis.session.Configuration#typeAliasRegistry.typeAliases
       */
      typeAliasesElement(root.evalNode("typeAliases"));
  • 解析插件
      /**
       * 解析我们的插件(比如分页插件)
       * mybatis自带的
       * Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
       ParameterHandler (getParameterObject, setParameters)
       ResultSetHandler (handleResultSets, handleOutputParameters)
       StatementHandler (prepare, parameterize, batch, update, query)
       解析到:org.apache.ibatis.session.Configuration#interceptorChain.interceptors
       */
      pluginElement(root.evalNode("plugins"));
  • 设置settings
      // 设置settings 和默认值
      settingsElement(settings);
  • 解析mybatis环境
      /**
       * 解析mybatis环境
       <environments default="dev">
       <environment id="dev">
       <transactionManager type="JDBC"/>
       <dataSource type="POOLED">
       <property name="driver" value="${jdbc.driver}"/>
       <property name="url" value="${jdbc.url}"/>
       <property name="username" value="root"/>
       <property name="password" value="Zw726515"/>
       </dataSource>
       </environment>

       <environment id="test">
       <transactionManager type="JDBC"/>
       <dataSource type="POOLED">
       <property name="driver" value="${jdbc.driver}"/>
       <property name="url" value="${jdbc.url}"/>
       <property name="username" value="root"/>
       <property name="password" value="123456"/>
       </dataSource>
       </environment>
       </environments>
       *  解析到:org.apache.ibatis.session.Configuration#environment
       *  在集成spring情况下由 spring-mybatis提供数据源 和事务工厂
       */
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
  • 解析数据库厂商
      /**
       * 解析数据库厂商
       <databaseIdProvider type="DB_VENDOR">
       <property name="SQL Server" value="sqlserver"/>
       <property name="DB2" value="db2"/>
       <property name="Oracle" value="oracle" />
       <property name="MySql" value="mysql" />
       </databaseIdProvider>
       *  解析到:org.apache.ibatis.session.Configuration#databaseId
       */
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
  • 解析类型处理器
      /**
       * 解析我们的类型处理器节点
       * <typeHandlers>
       <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
       </typeHandlers>
       解析到:org.apache.ibatis.session.Configuration#typeHandlerRegistry.typeHandlerMap
       */
      typeHandlerElement(root.evalNode("typeHandlers"));
  • 解析mapper文件
      /**
       * 解析mapper文件(SQL映射文件)
       *
       resource:来注册我们的class类路径下的
       url:来指定我们磁盘下的或者网络资源的
       class:
       若注册Mapper不带xml文件的,这里可以直接注册
       若注册的Mapper带xml文件的,需要把xml文件和mapper文件同名 同路径
       -->
       <mappers>
       <mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
       <mapper class="com.tuling.mapper.DeptMapper"></mapper>


       <package name="com.tuling.mapper"></package>
       -->
       </mappers>
       * package 1.解析mapper接口 解析到:org.apache.ibatis.session.Configuration#mapperRegistry.knownMappers
       2.
       */
      mapperElement(root.evalNode("mappers"));

到这里配置文件就解析完了,mybatis会根据configuration对象创建SqlSessionFactory类的对象。

SQL映射文件解析

上面解析全局配置文件的最后一行代码就是解析SQL映射文件的入口,下面我们来分享一下SQL映射文件的解析。
mapperElement方法中首先判断注册SQL映射的方式(通过package、resource、url还是class),然后再去解析对应的mapper文件。
在这里插入图片描述
parse方法中首先会判断mapper文件是否被加载过,如果被加载过就不需要再次解析了。

  • configurationElement方法中会解析mapper文件中的各个节点;
  • cacheRefElement(context.evalNode(“cache-ref”)):处理缓存;
  • parameterMapElement(context.evalNodes(“/mapper/parameterMap”)):处理sql参数;
  • resultMapElements(context.evalNodes(“/mapper/resultMap”)):处理返回值;
  • sqlElement(context.evalNodes(“/mapper/sql”)):解析sql节点;
  • buildStatementFromContext(context.evalNodes(“select|insert|update|delete”)):解析sql语句(insert/update/delete/select)
    在这里插入图片描述
  /**
   * 方法实现说明:解析我们的<mapper></mapper>节点
   * @param context document节点
   */
  private void configurationElement(XNode context) {
    try {
      //解析namespace属性
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.isEmpty()) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }

      //保存我们当前的namespace  并且判断接口完全类名==namespace
      builderAssistant.setCurrentNamespace(namespace);

//
//      解析我们的缓存引用
//     说明我当前的缓存引用和DeptMapper的缓存引用一致
//      <cache-ref namespace="com.tuling.mapper.DeptMapper"></cache-ref>
//      解析到org.apache.ibatis.session.Configuration#cacheRefMap<当前namespace,ref-namespace>
//      异常下(引用缓存未使用缓存):org.apache.ibatis.session.Configuration#incompleteCacheRefs
      cacheRefElement(context.evalNode("cache-ref"));

//      解析我们的cache节点
//        <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
//        解析到:org.apache.ibatis.session.Configuration#caches
//      org.apache.ibatis.builder.MapperBuilderAssistant#currentCache
      cacheElement(context.evalNode("cache"));

      //解析paramterMap节点(该节点mybaits3.5貌似不推荐使用了)
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));

      //解析我们的resultMap节点
      //解析到:org.apache.ibatis.session.Configuration#resultMaps
      // 异常 org.apache.ibatis.session.Configuration#incompleteResultMaps
      resultMapElements(context.evalNodes("/mapper/resultMap"));

      /**
       * 解析我们通过sql节点
       *  解析到org.apache.ibatis.builder.xml.XMLMapperBuilder#sqlFragments
       *   其实等于 org.apache.ibatis.session.Configuration#sqlFragments
       *   因为他们是同一引用,在构建XMLMapperBuilder 时把Configuration.getSqlFragments传进去了
       */
      sqlElement(context.evalNodes("/mapper/sql"));

      /**
       * 解析我们的select | insert |update |delete节点
       * 解析到org.apache.ibatis.session.Configuration#mappedStatements
       */
      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);
    }
  }

我们看一下buildStatementFromContext方法,这个方法最终会把节点解析成MappedStatement对象
在这里插入图片描述
下面3张图都是一个方法中的代码
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上面一张图中的createSqlSource方法会解析出Sql,接下来我们看下此方法是怎么解析的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最后解析出来的sql如下图所示:
在这里插入图片描述
到这里sql就解析完成了。

Mybatis中关于sql解析的一些类:

  • XMLConfigBuilder:解析mybatis中configLocation属性中的全局xml文件,内部会使用XMLMapperBuilder解析各个xml文件
  • XMLMapperBuilder:遍历mybatis中mapperLocations属性中的xml文件中每个节点的Builder,比如user.xml,内部会使用XMLStatementBuilder处理xml中的每个节点
  • XMLStatementBuilder:解析mapper配置文件中的节点,如select、insert等,内部会使用XMLScriptBuilder处理节点的sql部分,遍历产生的数据会放到Configuration的mappedStatements属性中
  • XMLScriptBuilder:解析xml中各个节点sql部分的Builder;

Configuration中使用map存储MappedStatement对象,key是mapper接口中的方法名;MappedStatement中存储的信息如下:
在这里插入图片描述sqlSource是解析出来的sql。
在这里插入图片描述

XMLScriptBuilder类结构如下图所示。这个类中有许多继承了NodeHandler接口的内部类,在解析动态sql的时候,会使用whereHandler去解析where节点,IfHandler解析if节点,其它的类似。
在这里插入图片描述

下面给出的是一条update语句,parseDynamicTags的解析流程如下:

<update id="update" parameterType="org.format.dynamicproxy.mybatis.bean.User">
    UPDATE users
    <trim prefix="SET" prefixOverrides=",">
        <if test="name != null and name != ''">
            name = #{name}
        </if>
        <if test="age != null and age != ''">
            , age = #{age}
        </if>
        <if test="birthday != null and birthday != ''">
            , birthday = #{birthday}
        </if>
    </trim>
    where id = ${id}
</update>

parseDynamicTags方法的返回值是一个List,也就是一个Sql节点集合

  1. 首先根据update节点(Node)得到所有的子节点,分别是3个子节点
    • 文本节点 UPDATE users
    • trim子节点
    • 文本节点 \n where id = #{id}
  2. 遍历各个子节点
    • 如果节点类型是文本或者CDATA,构造一个TextSqlNode或StaticTextSqlNode
    • 如果节点类型是元素,说明该update节点是个动态sql,然后会使用NodeHandler处理各个类型的子节点
  3. 遇到子节点是元素的话,重复以上步骤

参考

  1. Mybatis官方文档
  2. Mybatis视频

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/639701.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JDK11 官网下载(提供网盘下载资源)

目录 引言一、Oracle&#xff08;甲骨文&#xff09;二、JDK11下载1.JDK11下载入口2.JDK版本说明3.JDK11下载前说明4.JDK11下载 三、网盘下载1.资源提供说明2.资源列表清单&#xff08;持续更新中...&#xff09;3.获取方式 总结 引言 我们要学习 Java 语言去开发 Java 程序&a…

k8s 基本架构

k8s 中支持的 node 数 和 pod 数 k8s 也是逐步发展过来的&#xff0c;来看看以前和现在支持的 node 数 和 pod 数对比 node 即 节点 &#xff0c; 早期的 k8s 版本能够支持 100 台节点&#xff0c;现在 k8s 可以支持到 2000 台了 pod 数&#xff0c;早期的版本可以支持 1000 …

Android 自定义View 之 Dialog弹窗

Dialog弹窗 前言正文一、弹窗视图帮助类二、弹窗控制类三、监听接口四、样式五、简易弹窗六、常规使用七、简易使用八、源码 前言 在日常开发中用到弹窗是比较多的&#xff0c;常用于提示作用&#xff0c;比如错误操作提示&#xff0c;余额不足提示&#xff0c;退出登录提示等&…

linux 内核版本和发行版本

当要明确自己的Linux系统的版本号时&#xff0c;大多数情况是用命令确定Linux内核版本的。不过这个还是要与CentOS的版本号&#xff08;就是你使用的Linux系统的发行版本&#xff09;区分开来&#xff0c;这两个不是一个东西。 一、发行版本号 比如当时安装CentOS时&#x…

【Python】集合 set ① ( 集合定义 | 集合特点 | 代码示例 - 集合定义 )

文章目录 一、集合特点二、集合定义三、代码示例 - 集合定义 一、集合特点 在之前 的博客中 介绍了 列表 / 元组 / 字符串 数据容器 , 列表 支持 定义后 , 增加元素 / 修改元素 / 删除元素 , 并且 列表中可以存储 重复 / 有序 的元素 ;元组 定义后 不能 进行 增加元素 / 修改元…

(转载)有导师学习神经网络的回归拟合(matlab实现)

神经网络的学习规则又称神经网络的训练算法&#xff0c;用来计算更新神经网络的权值和阈值。学习规则有两大类别&#xff1a;有导师学习和无导师学习。在有导师学习中&#xff0c;需要为学习规则提供一系列正确的网络输入/输出对(即训练样本),当网络输入时&#xff0c;将网络输…

对于Promise的理解

1.什么是回调地狱 多层异步函数的嵌套叫做回调地狱 代码1&#xff1a; setTimeout(() > {console.log(吃饭);setTimeout(() > {console.log(睡觉);setTimeout(() > {console.log(打豆豆);}, 1000);}, 2000);}, 3000); 代码2: 通过id获取用户名,通过用户名获取邮箱…

如何自动识别快递单号和批量查询的方法

最近有很多朋友问我&#xff0c;有没有办法批量查询快递单号&#xff0c;查询该快递单号的所有物流发货信息&#xff1f;今天小编就来分享一个实用的查询技巧&#xff0c;教大家轻松查询多个快递单号&#xff0c;还可以一键保存查询数据&#xff0c;一起来看看吧。 首先今天我们…

PoseNet深度网络进行6D位姿估计的训练,python3实现

0.相关github网址 原版github代码-caffe实现tensorflow实现&#xff0c;相关版本较低&#xff0c;python2&#xff0c;本文根据此代码迁移到python3上面。pytorch实现&#xff0c;但将骨干模型从goglenet改成了resnet&#xff0c;实验效果得到提升&#xff0c;但没公布预训练权…

快递单号一键批量查询的具体操作方法和步骤

最近做电商的朋友对一个话题很感兴趣&#xff1a;如何批量查询快递单号&#xff1f;今天&#xff0c;小编给你安利一款软件&#xff1a;固乔快递查询助手&#xff0c;支持大量快递单号的批量查询。下面我们来看看批量查询的具体操作方法。 小伙伴们需要先在“固乔科技”的官网上…

session与cookie的来源与区别

目录 1.什么是HTTP&#xff1f; 2.cookie 3.session 4.cookie和session的区别 如果你对于session 和cookie 只有一点模糊理解&#xff0c;那么此文章能帮你更深入理解session和cookie &#xff0c;依旧和上篇文章一样&#xff0c;我们采用问题的方式来一步步探索&#xff0…

第七章 测试

文章目录 第七章 测试7.1 编码7.1.1 选择程序设计语言1. 计算机程序设计语言基本上可以分为汇编语言和高级语言2. 从应用特点看&#xff0c;高级语言可分为基础语言、结构化语言、专用语言 7.1.2 编码风格 7.2 软件测试基础7.2.1 软件测试的目标7.2.2 软件测试准则7.2.3 测试方…

JVM基础面试题及原理讲解

基本问题 介绍下 Java 内存区域&#xff08;运行时数据区&#xff09;Java 对象的创建过程&#xff08;五步&#xff0c;建议能默写出来并且要知道每一步虚拟机做了什么&#xff09;对象的访问定位的两种方式&#xff08;句柄和直接指针两种方式&#xff09; 拓展问题 Strin…

Flutter Widget 生命周期 key探究

Widget 在Flutter中&#xff0c;一切皆是Widget&#xff08;组件&#xff09;&#xff0c;Widget的功能是“描述一个UI元素的配置数据”&#xff0c;它就是说&#xff0c;Widget其实并不是表示最终绘制在设备屏幕上的显示元素&#xff0c;它只是描述显示元素的一个配置数据。 …

分库分表 21 招

&#xff08;一&#xff09;好好的系统&#xff0c;为什么要分库分表&#xff1f; 咱们先介绍下在分库分表架构实施过程中&#xff0c;会接触到的一些通用概念&#xff0c;了解这些概念能够帮助理解市面上其他的分库分表工具&#xff0c;尽管它们的实现方法可能存在差异&#…

自动化测试框架seldom

创建项目 | seldom文档 这个框架还是不错的&#xff0c;一直在优化&#xff0c;测试框架里的功能这里都有了。 seldom继承unittest单元测试框架&#xff0c;可以用来做UI和接口自动化项目。 安装 pip install seldom 创建项目 > seldom -P mypro 创建测试用例 # tes…

第8章 维护

文章目录 第8章 维护一、软件交付使用的工作二、软件交付使用的方式1) 直接方式2) 并行方式3) 逐步方式 8.1 软件维护的定义1、软件维护的定义2、软件维护的原因3、软件维护的类型1、改正性维护2、适应性维护3、完善性维护4、预防性维护 8.2 软件维护的特点8.2.1结构化维护和非…

12.异常-Exception|Java学习笔记

文章目录 异常介绍异常体系图一览运行时异常编译异常异常处理异常处理的方式try-catch 异常处理throws 异常处理注意事项和使用细节 自定义异常自定义异常的步骤 throw和throws的区别 异常介绍 基本概念&#xff1a;Java语言中&#xff0c;将程序执行中发生的不正常情况称为“…

【TCP/IP】多进程服务器的实现(进阶) - 多进程服务器模型及代码实现

经过前面的铺垫&#xff0c;我们已经具备实现并发服务器的基础了&#xff0c;接下来让我们尝试将之前的单任务回声服务器改装成多任务并发模式吧&#xff01; 多任务回声服务器模型 在编写代码前&#xff0c;先让我们大致将多任务&#xff08;回声&#xff09;服务器的模型抽象…

mac下部署和访问 Kubernetes 仪表板(Dashboard)

简介 Dashboard 是基于网页的 Kubernetes 用户界面。 你可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中&#xff0c;也可以对容器应用排错&#xff0c;还能管理集群资源。 你可以使用 Dashboard 获取运行在集群中的应用的概览信息&#xff0c;也可以创建或者修改 Kub…