PageHelper引发的幽灵数据问题解析

 更新时间:2023年04月19日 14:45:13   作者:java旭阳  
这篇文章主要为大家介绍了PageHelper引发的幽灵数据问题解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

最近测试反馈一个问题,某个查询全量信息的接口,有时候返回全量数据,符合预期,但是偶尔又只返回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的工作原理后,反复检查代码,都没有调用过startPagedebug查看ThreadLocal中也没有分页信息啊,懵逼中。那我看看别人写的添加分页参数的代码吧,不看不知道,一看吓一跳。

原来有位“可爱”的同事竟然在查询后,加了一个分页,就是把分页信息放到线程的ThreadLocal中。

那大家是不是有疑问,丁是丁,矛是矛,你的线程关我何事?这就要说到我们的tomcat了。

Tomcat请求流程

其实这就涉及到我们的tomcat相关知识了,我们一个浏览器发一个接口请求,经过我们的tomcat的,究竟是一个什么样的流程呢?

  • 客户端发送HTTP请求到Tomcat服务器。
  • TomcatHTTP连接器(Connector)接收到请求,将连接请求交给线程池Executor处理,解析它,然后将请求转发给对应的Web应用程序。
  • Tomcat的Web应用程序容器(Container)接收到请求,根据请求的URL找到对应的Servlet

关于tomcat中使用线程池提交浏览器的连接请求的源码如下:

从而得知,你的连接请求是从线程池从拿的,而拿到的这个线程恰好是一个“脏线程”,在ThreadLocal中放了分页信息,导致你这边出现问题。

总结

后来追问了同事具体原因,才发现是粗心导致的。有些bug总是出现的莫名其妙,就像生活一样。所以关键的是我们在使用一些开源框架的时候一定要掌握底层实现的原理、核心的机制,这样才能够在解决一些问题的时候有据可循。

以上就是PageHelper引发的幽灵数据问题解析的详细内容,更多关于PageHelper幽灵数据的资料请关注脚本之家其它相关文章!

相关文章

  • Springboot+AOP实现返回数据提示语国际化的示例代码

    Springboot+AOP实现返回数据提示语国际化的示例代码

    这篇文章主要介绍了Springboot+AOP实现返回数据提示语国际化的示例代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-07-07
  • IntelliJ IDEA配置java环境及解决IDEA不能直接运行单个JAVA文件的问题

    IntelliJ IDEA配置java环境及解决IDEA不能直接运行单个JAVA文件的问题

    这篇文章主要介绍了IntelliJ IDEA配置java环境及解决IDEA不能直接运行单个JAVA文件的问题,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • Java进阶学习之如何开启远程调式

    Java进阶学习之如何开启远程调式

    Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,这篇文章主要介绍了Java进阶学习之如何开启远程调式的相关资料,需要的朋友可以参考下
    2025-03-03
  • 为何HashSet中使用PRESENT而不是null作为value

    为何HashSet中使用PRESENT而不是null作为value

    这篇文章主要介绍了为何HashSet中使用PRESENT而不是null作为value,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • SpringBoot3利用AOP实现IP黑名单功能

    SpringBoot3利用AOP实现IP黑名单功能

    在Web应用开发中,控制对特定IP地址的访问权限是一个常见的需求,通过实现IP黑白名单功能,我们可以允许某些IP地址访问应用,同时拒绝其他IP地址的访问,本文将详细介绍SpringBoot3利用AOP实现IP黑名单功能,并附上相应的代码片段,需要的朋友可以参考下
    2024-09-09
  • Java contains用法示例

    Java contains用法示例

    这篇文章主要介绍了Java contains的用法示例,帮助大家更好的理解和学习Java,感兴趣的朋友可以了解下
    2020-11-11
  • 远程调用@FeignClient注解属性使用详解

    远程调用@FeignClient注解属性使用详解

    这篇文章主要为大家介绍了远程调用@FeignClient注解属性使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • 关于Spring项目对JDBC的支持与基本使用详解

    关于Spring项目对JDBC的支持与基本使用详解

    这段时间一直在观看Spring框架,所以下面这篇文章主要给大家介绍了关于Spring项目对JDBC的支持与基本使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-11-11
  • 反编译jar实现的三种方式

    反编译jar实现的三种方式

    本文主要介绍了反编译jar实现的三种方式,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • Spring Boot整合SSE实时通信的问题小结

    Spring Boot整合SSE实时通信的问题小结

    本文介绍了服务器发送事件(Server-Sent Events,SSE)技术,其主要特点包括单向数据流、自动重连、自定义事件类型等,SSE适用于实时更新场景,如新闻推送、评论系统等,感兴趣的朋友跟随小编一起看看吧
    2025-01-01

最新评论