mybatis系列-tkmybatis-10-pagehelper分页原理及源码分析

2024-06-19 18:32

本文主要是介绍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分页原理及源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1075815

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

MyBatis常用XML语法详解

《MyBatis常用XML语法详解》文章介绍了MyBatis常用XML语法,包括结果映射、查询语句、插入语句、更新语句、删除语句、动态SQL标签以及ehcache.xml文件的使用,感兴趣的朋友跟随小... 目录1、定义结果映射2、查询语句3、插入语句4、更新语句5、删除语句6、动态 SQL 标签7、ehc

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

MyBatis延迟加载与多级缓存全解析

《MyBatis延迟加载与多级缓存全解析》文章介绍MyBatis的延迟加载与多级缓存机制,延迟加载按需加载关联数据提升性能,一级缓存会话级默认开启,二级缓存工厂级支持跨会话共享,增删改操作会清空对应缓... 目录MyBATis延迟加载策略一对多示例一对多示例MyBatis框架的缓存一级缓存二级缓存MyBat

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三

Redis中Set结构使用过程与原理说明

《Redis中Set结构使用过程与原理说明》本文解析了RedisSet数据结构,涵盖其基本操作(如添加、查找)、集合运算(交并差)、底层实现(intset与hashtable自动切换机制)、典型应用场... 目录开篇:从购物车到Redis Set一、Redis Set的基本操作1.1 编程常用命令1.2 集

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的