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

相关文章

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

解决1093 - You can‘t specify target table报错问题及原因分析

《解决1093-Youcan‘tspecifytargettable报错问题及原因分析》MySQL1093错误因UPDATE/DELETE语句的FROM子句直接引用目标表或嵌套子查询导致,... 目录报js错原因分析具体原因解决办法方法一:使用临时表方法二:使用JOIN方法三:使用EXISTS示例总结报错原

MyBatis-Plus通用中等、大量数据分批查询和处理方法

《MyBatis-Plus通用中等、大量数据分批查询和处理方法》文章介绍MyBatis-Plus分页查询处理,通过函数式接口与Lambda表达式实现通用逻辑,方法抽象但功能强大,建议扩展分批处理及流式... 目录函数式接口获取分页数据接口数据处理接口通用逻辑工具类使用方法简单查询自定义查询方法总结函数式接口

MySQL深分页进行性能优化的常见方法

《MySQL深分页进行性能优化的常见方法》在Web应用中,分页查询是数据库操作中的常见需求,然而,在面对大型数据集时,深分页(deeppagination)却成为了性能优化的一个挑战,在本文中,我们将... 目录引言:深分页,真的只是“翻页慢”那么简单吗?一、背景介绍二、深分页的性能问题三、业务场景分析四、

MyBatis中$与#的区别解析

《MyBatis中$与#的区别解析》文章浏览阅读314次,点赞4次,收藏6次。MyBatis使用#{}作为参数占位符时,会创建预处理语句(PreparedStatement),并将参数值作为预处理语句... 目录一、介绍二、sql注入风险实例一、介绍#(井号):MyBATis使用#{}作为参数占位符时,会

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

MyBatis-Plus 中 nested() 与 and() 方法详解(最佳实践场景)

《MyBatis-Plus中nested()与and()方法详解(最佳实践场景)》在MyBatis-Plus的条件构造器中,nested()和and()都是用于构建复杂查询条件的关键方法,但... 目录MyBATis-Plus 中nested()与and()方法详解一、核心区别对比二、方法详解1.and()

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.