本文主要是介绍mybatis系列-tkmybatis-10-pagehelper分页原理及源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
最近在写Mybatis系列文章,pageHelper在物理分页上用的比较多,这里就通过源码对它的原理进行分析
tkmybatis源码版本:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
目录
一、使用案例:
二、整体流程:
三、源码解析:
1、添加mybatis拦截器
a、加载PageHelperAutoConfiguration配置
b、添加mybatis拦截器
2、执行分页查询
a、Page实例创建
b、查询操作
3、拦截器是怎么生效的
一、使用案例:
参考《003-数据库-tkmybatis-04-SpringBoot整合TkMybatis+PageHelper实现分页查询》
二、整体流程:
pageHelper实现物理分页的流程大概分为以下几步:
-
在依赖包pagehelper-spring-boot-autoconfigure.jar中的spring.factories文件内默认配置了jar包被Spring加载后自动加载到容器中的配置类com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration,这是一切的起点(可参考Spring如何通过SPI机制实现服务动态配置)
-
在PageHelperAutoConfiguration配置类中的@PostConstruct方法里为mybatis的每一个SqlSessionFactory中的Configuration添加mybatis的拦截器
-
执行查询操作
-
调用拦截StatementHandler(Sql语法的构建处理)方法,按照物理库的不同重构SQL实现分页
-
返回查询结果
三、源码解析:
1、添加mybatis拦截器
像整体流程中说的那样,Spring加载pageHelper的时候,pageHelper会首先通过配置类PageHelperAutoConfiguration,向SqlSessionFactory添加mybatis的拦截器,我们来看下具体过程。
a、加载PageHelperAutoConfiguration配置
如下图所示,pagehelper-spring-boot-autoconfigure.jar中的spring.factories设置了org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration,所以Spring在加载该jar包的时候,就会将该PageHelperAutoConfiguration类作为一个配置bean加入到容器中。
b、添加mybatis拦截器
我们来分析下PageHelperAutoConfiguration类,看下是怎么添加拦截器的。如下图所示,PageHelperAutoConfiguration类注解有几个限制,必须存在SqlSessionFactory类的实例才会生效,在mybatis配置完成后才开始执行。
重点看下 @PostConstruct注解的方法,在该bean完成参数注入后,容器会调用该方法。
注意这里会有多个SqlSessionFactory,像mybatis实现多数据源操作的时候,就会添加多个SqlSessionFactory。所以在PageHelper中会对每个SqlSessionFactory都添加拦截。
到这里,拦截器就添加完成了,拦截器内进行了什么操作,我们在查询的时候再分析。
2、执行分页查询
a、Page实例创建
下图就是我们执行分页查询时写的代码,PageHelper.startPage这个静态方法执行后,后续查询就会返回分页的结果。
跟进去startPage,看里面做了什么。
如下图所示,startPage方法,创建了Page对象,然后通过setLocalPage方法,将该对象放到了ThreadLocal中(为了保证线程安全,即一个线程一个Page对象用于分页)
b、查询操作
设置好了Page对象,这个对象是怎么用的呢,我们继续看执行查询操作的时候,发生了什么。以上一步截图中的List<Orders> result = ordersMapper.selectAll()查询为例。我们在第1步添加的拦截器PageInterceptor中的intercept方法添加断点,会发现,执行selectAll操作的时候,系统进到了拦截器中断点的位置,我们来看下,拦截器做了些什么。(拦截器是怎么生效的,即mybatis是怎么调用的该拦截器的intercept方法,这个复杂一点,我们放到最后说,先看下intercept方法里做了些什么)
intercept的操作如下图所示,这里可以看到,Page是否设置,影响着我们是否进行分页。另外有一个需要注意,在查询总数的时候,即count函数里面,PageHelper会根据MappedStatement的Id和"_COUNT"后缀,创建唯一的标识countMsId,如果查询条件没有改变(分页条件除外),则该countMsId是一样的,PageHelper会将count查询的结果用countMsId作为key存在Map中,供下次查询使用,从而避免每次分页查询的时候都去在数据库中重新计算count的值。
再进一步看下pageQuery中是如何进行分页的。
在生成pageSql的时候,系统会根据数据库类型,选择不同的dialect进行pageSql的创建,比如我们这里用的是MySqlDialect。pageSql创建好后,后面执行的分页查询executor.query这些就是mybatis的查询操作了,我们这里就不细讲。
至此,我们就完成了查询Sql的修改以及分页查询。
3、拦截器是怎么生效的
在第2步中,还有个遗留问题,拦截器是怎么生效的?其实这部分属于mybatis的内容,这里简单分析一下。回看下第1步,在下面的函数中,pageHelper添加了拦截器。
我们分析下addInterceptor方法所属的类,该类中还有个pluginAll方法。该方法会在执行SQL的时候调用。我们知道,每次执行SQL,mybatis都会为该操作生成一个sqlsession的对象,sqlsession对象生成时会生成一个executor(sql的实际执行类,在前面我们也能看见,改造后的分页sql就是用该类的方法来执行实际查询的),在生成executor的时候,mybatis会依次调用interceptors中添加的拦截器(注意这个拦截器不是spring的拦截器)的plugin方法,对executor进行处理。
我们继续看下PageInterceptor这个PageHelper的拦截器,在plugin方法中做了什么。
如下图,在wrap方法中,创建了executor的代理类Plugin(可以参考JDK动态代理是如何实现的)。
executor在执行Sql处理的时候,就会跳转到Plugin的invoke方法中,返回该方法的结果。invoke方法如下,因为在创建代理类Plugin时就把PageInterceptor传入了进去,所以在invoke方法中,就实现了PageInterceptor的intercept方法对SQL操作进行处理。至此就解决了拦截器是怎么生效的问题,后续的intercept的内容,就是第2步b部分的内容了。
这篇关于mybatis系列-tkmybatis-10-pagehelper分页原理及源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!