关于mybatis:mybatis之脚本解析器

7次阅读

共计 3591 个字符,预计需要花费 9 分钟才能阅读完成。

mybatis 之脚本解析器

本篇次要来介绍一下 mybatis 的脚本解析性能,基于 mybatis 3.4.6。

知识点

  • 什么是脚本解析器
  • 解析原理

什么是脚本解析器

第一眼看到脚本解析器这个说法,你必定会一脸懵逼,这百度上都搜不到啊。没错,这是我集体定义的,为什么取名叫脚本解析器呢,因为我是依据代码中来取的,基于两点起因:

1、对应的包名叫:scripting

2、次要解析的是 xml 等文本内容,在我看来相当于脚本(sql 语句其实也是一种脚本)

介绍完名称由来之后,再来介绍一下它到底是什么。

咱们平时在应用 Mybatis 的时候,个别只有两步:

1、定义一个接口,个别是 xxxMapper,定义了一些 CURD 接口;

2、定义上一步 Mapper 接口对应的配置文件,个别是 xxxMapper.xml,而后在配置文件中定义各个 CURD 接口对应的 sql 就能够了;

在配置文件中,咱们个别都会用到动静 sql,比方

  <if test="title != null">
    AND title like #{title}
  </if>

再比方

  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>

当然还有 whereforeach 等罕用的标签。这些标签显著不是 sql 自身的语法能解析的,为什么咱们却能这么用呢?说到这里想必大家曾经晓得了,这就是脚本解析器施展的作用。

解析原理

在理解脚本解析器是什么后,咱们基于一个例子来看下它是怎么对脚本做解析的。先定义一个 Mapper

@Repository
public interface UserInfoMapper {List<UserInfo> select(String userName, String nickName);
}

再定义一个对应 UserInfoMapper.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mybatisanalyze.mapper.UserInfoMapper">

    <select id="select" resultType="com.example.mybatisanalyze.po.UserInfo">
        select * from user_info where user_name = #{userName}
        <if test="nickName !=''">
        AND nick_name = #{nickName}
        </if>
    </select>
    
</mapper>

例子定义结束,这里咱们能看到次要用到了 if <test = "">标签,接着来阐明一下 mybatis 是如何对下面的例子进行解析并生成最终 sql 的。

首先咱们找到这个类org.apache.ibatis.builder.xml.XMLMapperBuilder,看名字预计也猜到了,它是对咱们下面定义的 UserInfoMapper.xml 文件进行解析的。

能够看到,它就是在解析 mapper 节点,跟进去看下就会更清晰

这里对 mapper 下的各个标签进行了解析,咱们重点看 CURD 这一块,也就是buildStatementFromContext

能够看到遍历了每个节点进行解析,持续往下

能够看到这里对节点下的标签再做解析,这外面有很多细节的标签解析,比方应用 org.apache.ibatis.builder.xml.XMLIncludeTransformer#applyIncludes(org.w3c.dom.Node, java.util.Properties, boolean)include 标签做解析,比方对 selectKey 标签做解析,这部分不具体介绍,接着看org.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(org.apache.ibatis.session.Configuration, org.apache.ibatis.parsing.XNode, java.lang.Class<?>)

这个类十分重要,所有脚本解析必然通过它来产生 SqlSource,它提供了 xml 和注解形式的解析,尽管LanguageDriver 有另外一个派生类 RawLanguageDriver,实际上这个派生类曾经不必了。在XMLLanguageDriver 中会用到 XMLScriptBuilder 来做理论的脚本解析。

这里能够看到会产生两种类型的 SqlSource,别离为DynamicSqlSourceRawSqlSource,这里说一下什么是 DynamicSqlSource,就是指脚本中带有 ${} 这种占位符的或者带 ifwhere 等这些标签的,反正就是不能间接拿来用的 sql。重点看下这句

在这里会看到通过名称获取对应的处理器NodeHandler,对于每一种标签,mybatis 都定义了一种处理器

另外须要晓得的一点:标签类的节点类型是 Node.ELEMENT_NODE,文本类的是 Node.TEXT_NODE 或者 Node.CDATA_SECTION_NODE。什么意思呢?比方标签类的 <if> 这种就认为是节点元素,所以叫 Node.ELEMENT_NODE,而两头的内容,比方:select * from user_info where user_name = #{userName},这种就认为是文本类型的。这里执行完之后,就生产了一个 sqlNode,最终被包装成一个 SqlSource 返回,也就是说每一条 sql 标签(select|insert|update|delete)都会有本人的 SqlSource。在 org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode 解析完之后,最初会生成一个MappedStatement

并退出到 org.apache.ibatis.session.Configuration 的 mappedStatements 中,所以如果咱们要用,也能够间接去 Configuration 中去获取。

脚本解析完了,然而咱们的例子还没讲完,不是要联合 <if> 标签吗,那么哪里在应用呢?

之前咱们说执行器的时候,说到在执行的时候要获取到BoundSql,这又是什么玩意儿?别急,咱们来看一下

能够看到这个是从 MappedStatement 中获取的,也就是咱们下面解析完之后生成的那个货色。进入办法 getBoundSql 看下

看到了吗,这里最终用的就是之前生成的 SqlSource 来获取到的,这个 SqlSource 在这里只会有两种:RawSqlSourceDynamicSqlSource。说白了就是动态文本和动静文本,动态文本就不说了,间接拿到可用的 sql,而动静文本,则须要去解析

这里传入了理论的参数对象,所以动静文本短少的货色都具备了,间接能够解析出后果,在 rootSqlNode.apply(context); 这行就调用了理论的标签处理器进行解决,比方咱们例子里的就是IfSqlNode

能够看到,这里用了 ExpressionEvaluator 进行解决,直译就是表达式翻译器。在这外面用到了 OgnlCache 进行解决,最终用的是 Ognl 进行解决

OgnlCache只是为了进步性能,什么是 Ognl 呢?参考这篇文章。从 <if> 标签解决来看,为 true 就会加到对应的文本前面,当然如果有占位符的则会解析占位符。获取到 BoundSql 之后,脚本解析器根本工作都实现了。

这里能够看出 BoundSqlSqlSource的区别,能够了解为 BoundSql 就是带了理论参数对象的SqlSource,前面再由类型处理器去一一映射。

总结

脚本处理器内容还是挺多的,学完这一篇我置信大家都会有不少播种,甚至咱们能够自定义标签来做解决。另外咱们在应用 mybatis 的时候会发现 Ognl 包是在 mybatis 里的,其实是 maven 的一种打包形式。

参考资料

Ognl:https://www.cnblogs.com/ends-…

maven 将依赖包打进以后包:https://blog.csdn.net/yangguo…

正文完
 0