PageHelper引发的幽灵数据问题解析
作者:java旭阳 发布时间:2022-08-15 22:33:13
前言
最近测试反馈一个问题,某个查询全量信息的接口,有时候返回全量数据,符合预期,但是偶尔又只返回1条数据,简直就是“见鬼”了,究竟是为什么出现这样的“幽灵数据”呢?
大胆猜测
首先我们看了下这对代码的业务逻辑,非常的简单,总共没有几行代码,也没有分页逻辑,代码如下:
public List<SdSubscription> findAll() {
return sdSubscriptionMapper.selectAll();
}
那么究竟是咋回事呢?讲道理不可能出现这种情况的啊,不要慌,我们加点日志,将日志级别调整为DEBUG
,让日志飞一段时间。
public List<SdSubscription> findAll() {
log.info("find the sub start .....");
List<SdSubscription> subs = sdSubscriptionMapper.selectAll();
log.info("find the sub end .....");
return subs;
}
果不其然,日志中出现了奇奇怪怪的分页参数,如下图所示:
果然是PageHelper
这个开源框架搞的鬼,我想大家都用过吧,分页非常方便,那么究竟为什么别人都没问题,单单就我会出现问题呢?
PageHelper工作原理
为了回答上面的疑问,我们先看看PageHelper
框架的工作原理吧。
PageHelper
是一个开源的 MyBatis
分页插件,它可以帮助开发者在查询数据时,快速的实现分页功能。
PageHelper
的工作原理可以简单概括为以下几个步骤:
在需要进行分页的查询方法前,调用
PageHelper
的静态方法startPage()
,设置当前页码和每页显示的记录数。它会将分页信息放到线程的ThreadLocal
中,那么在线程的任何地方都可以访问了。当查询方法执行时,
PageHelper
会自动拦截查询语句,如果发现线程的ThreadLocal
中有分页信息,那么就会在其前后添加分页语句,例如MySQL
中的LIMIT
语句。查询结果将被包装在
Page
对象中返回,该对象包含分页信息和查询结果列表。在查询方法执行完毕后,会在
finally
中清除线程ThreadLocal
中的分页信息,避免分页设置对其他查询方法的影响。
PageHelper
的实现原理主要依赖于 * 技术和反射机制,通过拦截查询语句并动态生成分页语句,实现了简单、高效、通用的分页功能。具体源码在下图的类中,非常容易看懂。
明白了PageHelper
的工作原理后,反复检查代码,都没有调用过startPage
,debug
查看ThreadLocal
中也没有分页信息啊,懵逼中。那我看看别人写的添加分页参数的代码吧,不看不知道,一看吓一跳。
原来有位“可爱”的同事竟然在查询后,加了一个分页,就是把分页信息放到线程的ThreadLocal
中。
那大家是不是有疑问,丁是丁,矛是矛,你的线程关我何事?这就要说到我们的tomcat了。
Tomcat请求流程
其实这就涉及到我们的tomcat
相关知识了,我们一个浏览器发一个接口请求,经过我们的tomcat
的,究竟是一个什么样的流程呢?
客户端发送
HTTP
请求到Tomcat
服务器。Tomcat
的HTTP
连接器(Connector
)接收到请求,将连接请求交给线程池Executor
处理,解析它,然后将请求转发给对应的Web应用程序。Tomcat
的Web应用程序容器(Container
)接收到请求,根据请求的URL找到对应的Servlet
。
关于tomcat中使用线程池提交浏览器的连接请求的源码如下:
从而得知,你的连接请求是从线程池从拿的,而拿到的这个线程恰好是一个“脏线程”,在ThreadLocal
中放了分页信息,导致你这边出现问题。
来源:https://juejin.cn/post/7223590232730370108


猜你喜欢
- •readonly和const都是用来标识常量的[1]。•const可用于修饰class的field或者一个局部变量(local varia
- Android PopWindow 设置背景亮度的实例设置背景 /** * 设置添加屏幕的背景透明度 * @param bgAl
- 我们都知道在一些特殊的场景,我们需采用特殊的编码格式,如:UTF-8,但是系统默认的编码为ISO-8859-1那么我们就需要将编码转换为我们
- 前言标签(Label)控件是最常用的控件,在任何Windows应用程序中都可以中都可以看到标签控件。标签控件用于显示用户不能编辑的文件或图像
- 本文接上文“java反射之方法反射的基本操作方法”,利用反射了解下java集合中泛型的本质1、初始化两个集合,一个使用泛型,一个不使用Arr
- 本文实例讲述了Android编程之canvas绘制各种图形的方法。分享给大家供大家参考,具体如下:1、首先说一下canvas类:Class
- 基本原理:利用URLConnection获取要下载文件的长度、头部等相关信息,并设置响应的头部信息。并且通过URLConnection获取输
- 本文实例为大家分享了java导出百万以上数据的excel文件,供大家参考,具体内容如下1.传统的导出方式会消耗大量的内存,2003每个she
- 本文介绍了Android定时器Timer的停止和重启实现代码,分享给大家,具体如下:7月份做了一个项目,利用自定义控件呈现一幅动画,当时使用
- 1.为什么要用thrift js C#? 1.1 首先,js 通过 thrift 访问C#,实际上是一种
- 开发一个需要常住后台的App其实是一件非常头疼的事情,不仅要应对国内各大厂商的ROM,还需要应对各类的安全管家...虽然不断的研究各式各样的
- http interface从 Spring 6 和 Spring Boot 3 开始,Spring 框架支持将远程 HTTP 服务代理成带
- 将索引设置为只读可能听起来很奇怪,但在 Elasticsearch 中执行此类操作是可能的。想象一下这样一种情况,你特别需要限制对索引的写入
- Android 遍历SDCARD的文件夹并显示目录信息private String mResult = new String(); priv
- 本文实例讲述了C#分布式事务的超时处理的方法。分享给大家供大家参考。具体分析如下:事务是个很精妙的存在,我们在数据层、服务层、业务逻辑层等多
- 解决Long型数据转换成json格式时丢失精度最近项目中突然发现雪花算法生成的数据库主键id返回给前端时和本身的值不一致,于是后端进行断点调
- C# DateTime与时间戳的相互转换,包括JavaScript时间戳和Unix的时间戳。1. 什么是时间戳首先要清楚JavaScript
- 前言在Activity间传递的数据一般比较简单,但是有时候实际开发中也会传一些比较复杂的数据,本节一起来学习更多Activity间数据的传递
- 一、前言无论承接什么样的需求,是不是身边总有那么几个人代码写的烂,但是却时常有测试小姐姐过来聊天(求改bug)、有产品小伙伴送吃的(求写需求
- 一、maven * 搭建使用Nexus进行搭建,网上教程很多,不多赘述了。二、gradle配置在build.gradle文件的根节点中添加以下