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

相关文章

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

基于SpringBoot+Mybatis实现Mysql分表

《基于SpringBoot+Mybatis实现Mysql分表》这篇文章主要为大家详细介绍了基于SpringBoot+Mybatis实现Mysql分表的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录基本思路定义注解创建ThreadLocal创建拦截器业务处理基本思路1.根据创建时间字段按年进

将Mybatis升级为Mybatis-Plus的详细过程

《将Mybatis升级为Mybatis-Plus的详细过程》本文详细介绍了在若依管理系统(v3.8.8)中将MyBatis升级为MyBatis-Plus的过程,旨在提升开发效率,通过本文,开发者可实现... 目录说明流程增加依赖修改配置文件注释掉MyBATisConfig里面的Bean代码生成使用IDEA生

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI