Mybatis源码解析:SQL解析流程

网友投稿 740 2022-09-02

Mybatis源码解析:SQL解析流程

Mybatis源码解析:SQL解析流程

SQL解析

上一节我们提到在解析的过程中一个比较重要的点是,对每个sql进行解析并封装为SqlSource对象

sql定义的方式有很多种,比如用xml,@Select,@SelectProvider等来描述要执行的sql,针对不同的定义方式,mybatis定义了不同的SqlSource实现类

SqlSource接口只有一个方法,传入sql执行的参数,获取BoundSql

这个BoundSql我们在参数处理器这一节再分享,你目前只需要知道BoundSql经过参数处理器处理后就能获取到可以执行的sql

常见的SqlSource实现类的作用如下

SqlSource

作用

DynamicSqlSource

动态sql

ProviderSqlSource

@*Provider 注解 提供的 SQL ,这种注解在通用mapper中用的最多

RawSqlSource

静态sql

StaticSqlSource

仅会含有?的sql

在执行的过程中会把对应的SqlSource都转为StaticSqlSource,StaticSqlSource就是对BoundSql的一个简单封装

当然如果你想增加另外一种sql定义的方式,只需要实现SqlSource接口即可。

那么问题来了,既然新写了一种sql定义的方式,那么相应的解析程序也要重新实现,不然mybatis根本不知道如何把你定义的sql翻译为可以执行的sql,此时你只需要重写LanguageDriver接口即可,增加你自己的解析实现

网上就有其他大神,重写了LanguageDriver和SqlSource,利用velocity定义了一套sql的解析流程,但是用的人比较少哈。

org.mybatis.scripting mybatis-velocity 2.1.0

加入依赖后,写出来的sql如下所示,有兴趣的小伙伴可以玩玩。

LanguageDriver在mybatis中有两个默认实现

XMLLanguageDriver:默认的LanguageDriver,可以处理动态sql和静态sql RawLanguageDriver:只可以处理静态sql

那么静态sql和动态sql如何区分呢?

举个例子,下面这段sql会被解析为如下的语法树

select id, name, agefrom ${tableName} name = #{name} and age = #{age} order by

在mybatis中是用SqlNode来解析标签中的内容的,每个不同的标签交给不同的SqlNode来进行解析SqlNode的定义如下,当执行SqlNode#apply方法时,会把标签解析完的sql放到DynamicContext(当所有标签解析完后,就可以从DynamicContext或获取到只会含有#{}占位符的sql)

常见的SqlNode如下,基本上就是一种标签一个SqlNode,MixedSqlNode是一个比较特殊的标签,它是多个标签的一个组合,因为一个标签下可能有很多子标签,这些子标签会被合并为一个MixedSqlNode,典型的组合模式

一段sql描述会被解析为一个MixedSqlNode,然后基于MixedSqlNode来构建SqlSourceXMLScriptBuilder#parseScriptNode

如下为将sql描述转为sql标签的过程XMLScriptBuilder#parseDynamicTags

对应的内容和解析成的SqlNode的对应关系如下

内容

解析成的SqlNode

if标签中的内容

IfSqlNode

where标签中的内容

WhereSqlNode

含有${}占位符的静态文本

TextSqlNode

纯静态文本或含有#{}占位符

StaticTextSqlNode

从这个解析过程可以看出来,静态sql为纯静态文本或含有#{}占位符的sql,除此之外都是动态sql(如含有${}占位符号,含有动态sql标签)

静态sql会被封装为RawSqlSource,动态sql会被封装为DynamicSqlSource

可以看到静态sql在初始化的时候已经解析完成了,SqlSourceBuilder会将#{}替换为?,并将#{}中的内容转为ParameterMapping对象org.apache.ibatis.builder.SqlSourceBuilder#parse

ParameterMappingTokenHandler#handleToken

我们一般只在#{}中写属性值,但是它其实可以设置很多属性,因此需要转为ParameterMapping对象

name = #{name,jdbcType=VARCHAR,typeHandler=org.apache.ibatis.type.StringTypeHandler}

#{}占位符中可以写的属性如下

将#{}中的内容封装为ParameterMapping对象

StaticSqlSource其实就是对BoundSql的一个简单封装

动态sql在执行的时候,才会执行解析,解析的过程和静态sql类似

分析完静态sql和动态sql,我们可以发现静态sql的执行效率比动态sql的执行效率高,因为静态sql在初始化的时候已经解析完成了,动态sql在执行的时候才会解析

SqlNode解析流程

最后我们挑几个典型的SqlNode来分析一下解过程

StaticTextSqlNode为纯静态文本或含有#{}占位符的节点,所以直接把内容加到容器中就行

if标签中的表达式为true时,才会将子节点的内容加到DynamicContext中

foreach标签的解析过程比较麻烦,直接看解析后的结果

可以看到将foreach标签被替换为#{}占位符,后续会把sql中名字和值的映射关系放到BoundSql的additionalParameters

foreach标签的解析流程和设置参数的流程比较耗时间,因此当你的foreach标签中的属性过多时,性能会极速下降,这个需要特别注意,此时你可以选择BatchExecutor来执行sql

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Linux下 php7安装redis的方法(linux下的dns功能是通过什么实现的)
下一篇:在colab里怎样读取google drive数据
相关文章

 发表评论

暂时没有评论,来抢沙发吧~