mybatis分页插件_[Mybatis]-[基础支持层]-插件-第三方分页插件简单剖析

524cd5852872539c9475b06325c939f3.png
该系列文章针对 Mybatis 3.5.1 版本

在 mybatis 中允许针对 SQL 在执行前后进行扩展操作,而这些扩展操作也叫做插件。

在 Mybaits 中允许用插件来拦截的方法包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

实现一个 plugin 的 步骤如下

step1、插件实现类实现了 Interceptor 接口

step2、通过注解 @Intercepts 指定了该插件需要拦截的对象

step3、mybatis-config.xml 中的 <plugins> 下配置插件

一、第三方分页插件简单剖析

基于 pagehelper-5.1.8 工具进行剖析
github 仓库地址: https://github.com/pagehelper/Mybatis-PageHelper

1、分页插件使用案例

e08eed8000a7c8e2607fbde0eeb9c45a.png

如上述代码,SELECT * FROM user_info 查询的是所有的数据并未进行分页,
但是通过 PageHelper 工具对其进行包装,便可以实现分页查询。

2、分页插件实现思路

SQL 在 Mybatis 启动完成后已经被加载到了 Configuration 对象中。

Mybatis 中无论针对 ExecutorParameterHandlerResultSetHandlerStatementHandler 哪一种情的拦截,只要持有了Configuration 对象相当于有了执行 SQL 数据,更确切的说是持有了Mybatis运行期间所有的数据。
而在 Mybatis 运行期间 Configuration 对象会伴随整个执行流程,包括 ExecutorParameterHandlerResultSetHandlerStatementHandler 等环节,因为这些对象都直接或者间接的持有了 Configuration 对象实例。

分页插件的实现原理,就是针对原有的 select xxx 查询语句通过代码动态的为其增加 limit 的分页查询语句,以此达到不编写分页语句,直接通过代码定义就能达到分页的效果。

鉴于此能够知道,分页插件的实现需要在 SQL 执行前对SQL进行查询语句拼接,而通过拦截 ExecutorStatementHandler 便能够实现 SQL 执行前的动态编辑。

通常来说直接拦截 Executor 即可,因为在 Executor query 执行是,持有 MappedStatementResultHandler 的引用,相当于有了 SQL 更改和结果集操作的能力。

pageHelp 通过拦截 Executor#query 实现的分页插件。

3、分页插件拦截器主线逻辑代码

pageHelp 拦截器实现类 com.github.pagehelper.PageInterceptor

3.1、拦截器定义

  • 实现 org.apache.ibatis.plugin.Interceptor 接口
  • 注解 @Intercepts 定义拦截的对象方法

PageInterceptor 代码如下:

87edafc32d755da253d5ac92673ffafb.png

3.2、方法 intercept 主线逻辑

针对 Mybatis 中的 Interceptor 插件,代码实现都在 Inteceptor#intercept方法中,
来看一下 pageHelp 插件 PageInterceptor#intercept 主线逻辑代码

b5feedf5e311c9f43aae11facb2c01c2.png

通过上述插件代码能够知道分页插件的主线执行逻辑

  • 1、判断是否需要分页操作
  • 2、分页处理 or 普通查询处理
  • 3、返回结果集 分页处理对象再一次封装处理 or 普通查询返回结果不处理。

二、题外话

本次剖析,单纯只是只是为了了解Mybatis 插件的使用,以及如何实现分页插件,以及插件的分页应用。

这里简单对上面讲到的分页解析流程进行简单源码查看。

1、判断是否需要分页操作

判断是否需要分页查询的代码实现在 com.github.pagehelper.PageHelper#skip

代码如下:

7f90494fd72298bdc2fe1644242bd909.png

如上述代码,需要分页的添加:当前线程上下文中能够获取到 Page 对象。

Page 对象在使用 API 的时候会进行设置,如: PageHelper.startPage

2、分页处理 or 普通查询处理

在步骤一判断完是否需要分页后,会有两个代码分支,一个分支执行的原始的sql,一个分支需要完成分页操作,需要动态拼接 limit 关键字。

相关代码如下:

6ac2ac16295aabb77fd22cf7de2ad0ba.png

普通查询直接调用 Executor 进行 SQL 的执行

分页查询会借助 ExecutorUtils 将原始 SQL 拼接分页语句,然后调用 Executor 进行 SQL 的执行。

分页SQL的相关拼接可以参考 MySqlDialect#getPageSql,代码如下:

193c0a00769d3c8a21213c3c58ab396e.png

如上述代码,对原始 sql 进行 limit 关键字的拼接

更多细节,自行查阅代码。

3、分页后结果集的映射

回顾一下分页查询案例代码

d8fe0730cdab1cfd8f32464e3e983a1b.png

如上述代码,原始的 selectList 方法使用的是 List<UserInfo> 来接受,而通过分页插件包装之后,使用的 PageInfo<UserInfo> 来进行结果集的接收,

相关的处理代码在 Dialect#afterPage 中完成,而该代码的执行在分页插件 PageInterceptor 最后执行。

在执行处理 Dialect#afterPage 前,响应结果集已经映射到了 List<UserInfo> 中,而分页插件再次基础上有进行了PageInfo 的包装,相关代码
如下:

a5bcc79ecea5e385c2ab7f3821930652.png

如上述代码,判断当前查询是否为分页查询(线程上下文中有分页参数表示本次查询是分页查询),针对分页查询,对已有的结果集,在包装一层PageInfo对象。

三、总结

第三方分页插件,通过 Mybatis 本身提供的拓展点,也就是插件进行功能增强。

根据特定的条件判定本次查询是否需要分页

PageHelper 插件通过在线程上下文中存入分页参数,通过判断当前上下文是否存在分页参数,来判断是否是分页查询。

针对非分页查询,不进行处理,仍然按照 Mybatis 旧执行代码执行。

针对分页查询,根据分页参数,执行 count() 统计数量语句,同时对原始sql,在sql执行前,动态拼接 limit 关键字,达到分页查询的效果。

针对结果集,如果是原始非分页查询,不进行处理,如果是分页查询的结果集,在原有的结果集的基础上,进行一层新的对象包装,比如:PageHelper 中的 PageInfo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值