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

news/2024/11/8 14:37:10/

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

全局配置文件解析

  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 firstpropertiesElement(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 #631environmentsElement(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.knownMappers2.*/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  并且判断接口完全类名==namespacebuilderAssistant.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#incompleteCacheRefscacheRefElement(context.evalNode("cache-ref"));//      解析我们的cache节点
//        <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
//        解析到:org.apache.ibatis.session.Configuration#caches
//      org.apache.ibatis.builder.MapperBuilderAssistant#currentCachecacheElement(context.evalNode("cache"));//解析paramterMap节点(该节点mybaits3.5貌似不推荐使用了)parameterMapElement(context.evalNodes("/mapper/parameterMap"));//解析我们的resultMap节点//解析到:org.apache.ibatis.session.Configuration#resultMaps// 异常 org.apache.ibatis.session.Configuration#incompleteResultMapsresultMapElements(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.ppmy.cn/news/350520.html

相关文章

python方向键控制角色_教你快速用 Python 控制键盘和鼠标,彻底解放双手

你还在为在计算机上双击或提交表单而烦恼吗?今天就用python来控制键盘和鼠标,解放双手! 第一,Pyauogui库 我们可以首先安装pyauogui库,通过它我们可以编写一些Python脚本来控制鼠标和键盘。例如,可以定义鼠标的点击位置、键盘的输入时间等,实现所有要操作的软件应用程序…

Linux中软件仓库的搭建与虚拟化的部署及应用

目录 一、软件仓库搭建 搭建epel仓库 dnf第三方软件仓库的搭建与共享 二、虚拟化的部署 下载并且安装 虚拟机快照的应用 总结 一、软件仓库搭建 搭建epel仓库 什么是epel EPEL 的全称叫 Extra Packages for Enterprise Linux。EPEL 是由 Fedora 社区打造&#xff0c;…

7 款可替代 top 命令的工具

作者&#xff1a;JackTian 来源&#xff1a;公众号「杰哥的IT之旅」 ID&#xff1a;Jake_Internet 转载请联系授权&#xff08;微信ID&#xff1a;Hc220088&#xff09; 原文地址&#xff1a;7 款可替代 top 命令的工具&#xff01;(二) 大家好&#xff0c;我是杰哥。 上一篇文…

python软件的使用方法_分子绘图软件PyMOL使用教程(1)——软件介绍与安装

软件简介 PyMOL是由Warren Lyford DeLano编写的一个分子结构显示软件,由于生成的图片质量极高,受到了科研工作者的广泛好评;由PyMOL制作的图像屡次出现在Nature、Science等杂志的封面上,作者自己则称所有发表的蛋白质结构图像中,有1/4的是有PyMOL制作的。2010年,Schrding…

游戏美术师的火绝对不是捧出来的!不看不知道游戏模型师这么吃香

我们对于一款游戏最初的印象&#xff0c;绝不是来自游戏的玩法与内容。人是视觉动物&#xff0c;所以我们对于游戏最初的印象一定是它的画面够不够真实与优美&#xff0c;特效够不够炫酷和爆炸&#xff0c;界面够不够精美和友好&#xff0c;人物够不够漂亮&#xff0c;就像我们…

在linux终端绘图,Boxes - 在Linux终端中绘制ASCII艺术盒子和图形

原标题&#xff1a;Boxes - 在Linux终端中绘制ASCII艺术盒子和图形 Boxes是一个简单&#xff0c;可配置的命令行程序&#xff0c;可以在输入文本周围绘制任何类型的盒子。 它过滤文本并在其周围绘制形状 - 它实际上是一个文本过滤器。 实际上它被设计为与编辑器集成为文本过滤器…

【ROS学习】- PlotJuggler绘图工具的安装使用

写在前面&#xff1a; 以下内容来自ros官网&#xff1a;https://www.ros.org/news/2017/01/new-package-plotjuggler.html github链接&#xff1a;https://github.com/facontidavide/PlotJuggler 一、PlotJuggler 介绍 PlotJuggler&#xff0c;一个基于Qt的应用程序&#xff0…

Linux下如何删除长时间不使用的旧文件?

生物信息学习的正确姿势 NGS系列文章包括NGS基础、高颜值在线绘图和分析、转录组分析 &#xff08;Nature重磅综述|关于RNA-seq你想知道的全在这&#xff09;、ChIP-seq分析 &#xff08;ChIP-seq基本分析流程&#xff09;、单细胞测序分析 (重磅综述&#xff1a;三万字长文读懂…