mybatis源码(五)springboot pagehelper实现查询分页
1、背景
springboot的pagehelper插件能够实现对mybatis查询的分页管理,而且在使用时只需要提前声明即可,不需要修改已有的查询语句。使用如下:
之前对这个功能一直很感兴趣,但是一直没完整看过,今天准备详细梳理下。按照个人的习惯,我喜欢带着问题去看源码,这次浏览源码我希望可以了解以下两个问题:
1)分页插件什么时候被加载的
2)分页插件什么时候生效的,如何生效的
2、技巧(可跳过)
mybatis自身的功能原理这里我们不深入探讨,网上文章很多,我之前也写过类似的文章,这里为了防止文章杂乱,我们只关注感兴趣的点,即mybatis pagehelper分页原理,其它的一笔带过。另外因为mybatis查询生效都是用的代理对象。如果对源码不是很熟悉,很难第一时间找到打断点的位置。不过通过查看page的源码可以知道,最后的分页参数都放到了threadlocal对象里,如下:而最后使用该threadlocal时是一定会调用它的获取和移除方法,因此我们在它的这两个方法中加断点。等进入断点后再通过方法栈追踪其全流程。如下:
3、源码追踪流程
有了上述方法栈的查看,我们知道了查询的入口和大体流程,现在重新执行查询跟随源码脚步来详细看下分页的实现逻辑。首先是在代码中我们主动进行分页查询,如下:
前三行代码很简单,主要是从httpservletrequest中获取分页参数,并设置到当前线程的threadlocal变量中。第四行则是调用接口方法进行查询。在mybatis中,接口方法最终功能的实现主要是依靠MapperProxy代理类实现(代理类中包含接口和xml配置相关信息),所以接下来我们直接到MapperProxy中打断点追踪(至于mybatis如何关联接口和mapper.xml文件,MapperProxy如何生成等问题不是本文讨论重点,感兴趣的可以单独去查阅或者浏览我以前的相关文章)。
MapperProxy是代理对象,主要调用方法是invoke方法,所以在该方法中加断点,或者依靠第二步中的技巧查看整个查询的方法栈,随后再在MapperProxy对应的行中加断点。具体断点信息如下:
因为method不是Object类型,所以执行else里面的代码:
可以看到最后会执行PlainMethodInvoker对象里的invoke方法,所以我们接着到该方法中打断点并继续追踪查看:
可以看到调用SqlSessionTemplate对象的selectList方法,我们接着向下看:
再接着到DefaultSqlSession中继续查看:
可以看到,最后会调用CachingExecutor的query方法,但是这里要留意一点,那就是CachingExecutor是一个代理对象,执行代理对象方法首先要进入代理,并执行invoke方法。我们这里通过断点调试的步入查看执行哪个类的invoke方法:
可以看到最终是通过Plugin代理实现CachingExecutor对象query方法的调用。我们再接着向下看:
因为要执行的方法是查询方法,其是分页拦截器指定要拦截的方法类型,所以会进入拦截方法中。这里我标记了两个框,第一个是拦截,第二个是不进行拦截直接执行查询方法,因此可以推测分页逻辑是再拦截器中进行的。拦截器中会进行sql的改写,所以这里进入拦截器中进一步查看。拦截器对象为PageInterceptor,到这终于和本文的主题关联上了,接下来我们到分页拦截器中看一下:
可以看到源码中都加好注释了,看起来就更简单了,这里我们看下进入分页的条件:
可以看到最后获取的page对象实际上就是我们一开在代码中传入分页参数创建的page对象。因为page对象不为空,所以会返回false,进而不跳过分页逻辑。(另外这里要提醒下各位小伙伴,page对象继承了ArrayList,所以断点查看时看不得page里面的内容,只能看到其size为0。)
我们接着回到主流程向下看,随后会判断是否需要计算总数,默认创建page时需要计算总数,这里我们就不进入before方法查看了,里面逻辑比较简单。下面我们简单看下计算总数的逻辑:
可以看到其计算总数的sql比较精简,主要是根据查询的对象和条件直接计算总数。这里sql的解析和生成主要是依赖jsqlparser工具类实现(jsqlparser的使用可以参考我以前的文章),sql解析比较复杂,感兴趣的可以自行查看。接着我们再回到主流程,看下何时添加的分页查询参数:
可以看到最后再sql末尾加了limit分页参数,而这个sql的改写过程与计算count类似,都是通过jsqlparser工具实现的。
通过上面的流程,我们已经知道了分页插件如何生效的了。但是还有一个问题是分页插件如何被加载的。这个流程比较简单,我也是通过第二步的技巧逆推的全流程。下面我按照正常项目的加载顺利简单介绍下:
当我们在pom中引入pageHelper插件依赖并且在yml中配置分页相关的信息时,项目启动后就会主动的进行插件的初始化并注入到插件拦截器链里面。大体逻辑如下:
可以看到分页插件有个配置类,其在项目初始化的时候会创建分页拦截器,并调用Configuration进行添加,接着我们看下最后会将拦截器添加到哪里:
可以看到最终会添加到拦截器链对象的私有集合里。但是我们最终使用拦截器是在Plugin对象里用的,而不是在拦截器链里面,那Plugin如何使用到该拦截器的呢?
在拦截器链里有个pluginAll方法,它会封装拦截器成一个“链式”动态代理对象,代理类是Plugin,该方法会在创建Executor时执行(还记得前面源码里介绍这块的提醒吧,Executor是一个被动态代理的对象),通过pluginAll方法,将拦截器封装成链并将Executor放在链路最后一层。
可以看到通过pluginAll方法将拦截器封装成了一个链,下面再看一下Executor的创建就完全清晰了:
至此,我们两个问题都再浏览源码的过程中清晰了。
4、总结
1)该文章主要是探究sringboot分页插件实现的原理,所以对于mapper.xml与接口方法的整合和mybatis代理对象如何实现查看没有细讲,但是这部分也是查询过程中核心的代码。
2)由PageInterceptor分页拦截器拦截指定的查询请求,然后在拦截方法中调用PageHelper中的方法对sql进行改写,最后再进行提交。
2)无论计算总数还是重写分页sql,都是通过jsqlparser工具实现的(为了使得文章主题清晰,这里没有介绍sql的改写过程,jsqlparser的使用可以浏览我以前的文章)
3)Plugin是一个链式动态代理对象,最后一个节点是Executor被代理对象,前面的节点是Interceptor被代理对象。
4)再Plugin.wrap方法中会提取出拦截器里的signature标签,并保存在每个Plugin链式代理对象中。在被代理对象执行对应方法时,如果plugin代理对象包含对应的signature集合则说明当前被代理对象是拦截器,如果不包含signature或者signature标签没有拦截当前方法,则直接执行当前方法。
参考文章:
5分钟!彻底搞懂MyBatis插件+PageHelper原理 - 知乎
相关文章:

mybatis源码(五)springboot pagehelper实现查询分页
1、背景 springboot的pagehelper插件能够实现对mybatis查询的分页管理,而且在使用时只需要提前声明即可,不需要修改已有的查询语句。使用如下: 之前对这个功能一直很感兴趣,但是一直没完整看过,今天准备详细梳理下。按…...

【BUG】SpringBoot项目Long类型数据返回前端精度丢失问题
问题描述 后端再给前端返回数据,使用Long类型的时候存在精度丢失问题。 原因分析: 分布式项目中广泛使用雪花算法生成ID作为数据库表的主键,Long类型的雪花ID有19位,而前端接收Long类型用的是number类型,但是number…...

UI自动化Selenium find_elements和find_element的区别
# 如果获取的element是list,那么需要用find_elements方法;此方法会返回list,然后使用len() 方法,计算对象的个数; # find_element方法返回的不是list对象,所以导致没办法计算对象个数 # 1.返回值类型不同…...

【Android】Window和WindowManager
文章目录 理解Window和WindowManagerWindow和WindowManagerWindow的内部机制Window的添加过程Window的删除过程Window的更新过程 Window的创建过程Activity的Window创建过程Dialog的Window创建过程Toast的Window创建过程 理解Window和WindowManager Window是一个抽象类…...

如何解决 Python ModuleNotFoundError 错误
模块对于开发 Python 程序很重要。 使用模块,我们可以分离代码库的不同部分以便于管理。 使用模块时,了解它们的工作方式以及如何将它们导入我们的代码非常重要。 如果没有这种理解或错误,我们可能会遇到不同的错误。 此类错误的一个示例是…...

Day62.算法训练
718. 最长重复子数组 class Solution {public int findLength(int[] nums1, int[] nums2) {int max 0;int[][] dp new int[nums1.length][nums2.length];for (int i 0; i < nums1.length; i) {for (int j 0; j < nums2.length; j) {if (nums1[i] nums2[j]) {if (i …...

Linux smbd命令教程:如何配置和管理Samba服务器(附案例详解和注意事项)
Linux smbd命令介绍 smbd是Samba套件的一部分。smbd是一个服务器守护进程,为Windows客户端提供文件共享和打印服务。服务器使用SMB(或CIFS)协议为客户端提供文件空间和打印服务。这与LanManager协议兼容,可以为LanManager客户端提…...

音视频学习(十九)——rtsp收流(tcp方式)
前言 本文主要介绍以tcp方式实现rtsp拉流。 流程图 流程说明: 客户端发起tcp请求,如向真实相机设备请求,端口一般默认554;tcp连接成功,客户端与服务端开始rtsp信令交互;客户端收到play命令响应后,开启线…...

LangChain(0.0.340)官方文档三:Prompts上——自定义提示模板、使用实时特征或少量示例创建提示模板
文章目录 一、 Prompt templates1.1 langchain_core.prompts1.2 PromptTemplate1.2.1 简介1.2.2 ICEL1.2.3 Validate template 1.3 ChatPromptTemplate1.3.1 使用role创建1.3.2 使用MessagePromptTemplate创建1.3.3 自定义MessagePromptTemplate1.3.3.1 自定义消息角色名1.3.3.…...

【算法】合并K个升序链表
这道题主要考察的是归并排序,因为已经升序过了,更好理解了。 当然也可以采用分治的思路;或采用最小堆的思路;面试中校招同学写出一种即可,如果能全概览讲一下,就更加分了。 #############################…...

持续集成交付CICD:GitLab Webhook触发Jenkins流水线
目录 一、实验 1.Jenkins远程下载GiaLab仓库代码 2.curl远程触发Jenkins流水线 3.GitLab Webhook触发Jenkins流水线 二、问题 1.GitLab配置Webhook时报错 一、实验 1.Jenkins远程下载GiaLab仓库代码 (1) Jenkins添加选项参数 (2)添加字符参数 (3)查看构建参数情况 (4)添…...

计算机网络测试题
一 单项选择题(5分) 1、假设要发送的数据为101110,采用CRC的生成多项式是X31,试求应添加在数据后面的余数。(5分) 110 011(答案) 101 001 实际得分:5分 二 填空题(95分) 1、以下3个子地址块…...

vscode如何在没有网络的情况下安装插件
vscode如何在没有网络的情况下安装插件 start 遇到没有网络的电脑,无法直接从插件市场安装vscode的插件。写一下 vscode 插件离线安装的方法. 解决方案 目标电脑没有可以安装插件的网络,那我们只能在有网络的环境下载好我们的插件。然后拷贝软件到无…...

自定义类型:结构体、联合、枚举
目录 一、⾃定义类型:结构体 1.结构体类型 1. 1结构体类型的声明 结构体变量的创建和初始化 1.2 结构的特殊声明 1.3 结构的自引用 2. 结构体内存对齐 ①:对齐规则 ②:offsetof函数 ③:为什么存在内存对⻬? ④ 修改默认对⻬…...

HelpLook可以作为wordpress的替代品,帮助企业快速搭建博客
博客作为一个非常有价值的平台,在当今的数字时代具有重要的意义。对于个人和企业来说,选择一款适合自己需求的专业博客搭建软件至关重要。本篇文章将会通过对比两个专业的博客搭建软件——HelpLook和WordPress,看看为什么我说HelpLook可以作为…...

单片机实现数码管动态显示
动态显示的特点是将所有位数码管的段选线并联在一起,由位选线控制是哪一位数码管有效。这样一来,就没有必要每一位数码管配一个锁存器,从而大大地简化了硬件电路。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码…...

jbrowse基因组浏览器部署
一、jbrowse部署 1.1 jbrowse部署 sudo mkdir /var/www/jbrowse; sudo chown whoami /var/www/jbrowse; # cd into it cd /var/www/jbrowse;1.2 nginx配置 server {listen 80 default_server;listen [::]:80 default_server;server_name _;#root /var/w…...

神经网络 模型表示(一)
神经网络 模型表示 模型表示一 为了构建神经网络模型,我们需要首先思考大脑中的神经网络是怎样的?每一个神经元都可以被认为是一个处理单元/神经核(processing unit/Nucleus),它含有许多输入/树突(input/…...

【漏洞复现】智跃人力资源管理系统GenerateEntityFromTable.aspx接口存在SQL注入漏洞 附POC
漏洞描述 智跃人力资源管理系统是基于B/S网页端广域网平台,一套考勤系统即可对全国各地多个分公司进行统一管控,成本更低。信息共享更快。跨平台,跨电子设备。智跃人力资源管理系统GenerateEntityFromTable.aspx接口处存在SQL注入漏洞,攻击者可通过该漏洞获取数据库中的信…...

【matlab程序】画海洋流场
【matlab程序】画海洋流场 clear;clc; file ( ‘0227.nc’); latncread(file,‘latitude’); lonncread(file,‘longitude’); uncread(file,‘water_u’); vncread(file,‘water_v’); [x,y]meshgrid(lon,lat); xx’; yy’; interval4; figure (1) set(gcf,‘color’,[1 1 1…...

线性表 力扣67. 二进制求和
题目 67. 二进制求和 翻译 主要思路 核心思路是像竖式计算一样,不过需要将字符串a和b反转后逐位进行二进制计算得到字符串c,最后再将c反转就是答案 逐位计算的时候利用count,在将a和b当前位置数字相加后通过模2来决定字符串c对应位置的数…...

2312skia,13画布包入门
画矶包快速入门 CanvasKit是用比canvasAPI更高级功能集的Skia来绘画元素到canvas中的wasm模块. 最小应用 此例是个最小Canvaskit应用,它为一帧绘画一个圆角矩形.从unpkg.com中提取wasm二进制文件,但你也可自己构建和管理它. <canvas idfoo width300 height300></c…...

【网络安全技术】消息认证技术
一、哈希函数 1.安全性质 1)抗第一原像攻击(Preimage Resistance) 给定哈希后的值,很难找到哈希前的原消息。这很好理解,需要哈希函数具有单向性。 一个简单的例子就是密码存储系统,用户登录服务器需要…...

智慧安防三大信息技术:云计算、大数据及人工智能在视频监控EasyCVR中的应用
说到三大信息技术大家都很清楚,指的是云计算、大数据和人工智能,在人工智能(AI)快速发展的当下,例如常见的大数据分析、人工智能芯片生产的智能机器人等等,在工作、生活、教育、金融、科技、工业、农业、娱…...

接口测试基础知识
一、接口测试简介 什么是接口测试? 接口测试是测试系统组件间接口的一种测试,主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。 测试的重点: 检查数据的交换,传递和控制管理过程;检查系统间的相互…...

C++多线程之通过成员函数作为线程入口
说明: 通过类里面的函数作为线程入口,我个人难理解的地方在于给线程传递参数的时候,怎么找到Main函数。后面会做分析。 首先创建类:创建MyThread类,其中公有函数Main作为入口。这个类的传教比较简单,成员…...

word、excel文件转PDF(documents4j方式,简单)
1 documents4j方式 引入pom <dependency><groupId>com.documents4j</groupId><artifactId>documents4j-local</artifactId><version>1.1.12</version></dependency><dependency><groupId>com.documents4j</g…...

【Linux】:信号(三)捕捉
信号捕捉 一.sigaction1.基本使用2.sa_mask字段 二.可重入函数三.volatile四.SIGCHLD信号 承接上文 果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信…...

数据结构 / 队列 / 循环队列 / 概念
1. 定义 为充分利用向量空间,克服假溢出现象的方法是:将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列(Circular Queue)。循环队列是把顺序队列首尾相连,把存储…...

elasticsearch 内网下如何以离线的方式上传任意的huggingFace上的NLP模型(国内闭坑指南)
es自2020年的8.x版本以来,就提供了机器学习的能力。我们可以使用es官方提供的工具eland,将hugging face上的NLP模型,上传到es集群中。利用es的机器学习模块,来运维部署管理模型。配合es的管道处理,来更加便捷的处理数据…...