大家好,我是烤鸭:

上上周末上线到凌晨4点半,哭了,没想到问题竟然如此简单。最近又懒惰了,写了开头就一直放着了,今天终于补上。

问题日志

Error querying database. Cause: com.github.pagehelper.PageException: 被分页的语句已经包含了Top,不能再通过分页插件进行分页查询!

奇怪的是报错的语句并没有使用分页插件,只是一个简单的查询。

原因猜想

遇到问题第一时间回滚之后就好了,说明问题出现在这次提交。

跟pagehelper有关。

  • 修改pom文件,更改 pagehelper 相关依赖,导致的问题。并不是。
  • 修改了 pagehelper 的配置(配置类或者是yml)。也没有。
  • 排除法,针对此次提交的代码进行部分上线。

问题复现

最后发现确实有个地方,写法是这样的。

由于项目拆分,把原来直接查库的地方改成了http调用,但是改写的人并没有关注业务逻辑,导致分页代码没有注释。(虽然注释了也不对,http改写的方法也没支持分页参数)

PageHelper.startPage(pageNum,pageSize);
//注释dao,改为http调用
//xxxDao.selectXxx();
xxxHttp.select();

就这样分页在当前的线程没有使用,当这个线程执行别的Sql 查询的时候,就会有问题,具体看下下面的源码分析。

源码分析

我们的数据库是SqlServer,Mysql和SqlServer的分页实现是不一样的。

下面的演示项目里分别模拟了不同的场景。

主要还是看下分页和SqlServer的实现源码。

分页插件整个核心代码是在 PageInterceptor 这个拦截器里。

执行 PageHelper.startPage(pageNum,pageSize); 时会将分页参数放到TheadLocal里。

PageMethod

public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {Page<E> page = new Page<E>(pageNum, pageSize, count);page.setReasonable(reasonable);page.setPageSizeZero(pageSizeZero);//当已经执行过orderBy的时候Page<E> oldPage = getLocalPage();if (oldPage != null && oldPage.isOrderByOnly()) {page.setOrderBy(oldPage.getOrderBy());}setLocalPage(page);return page;
}

finally里执行的AfterAll()代码就是在执行之后清空 ThreadLocal

public Object intercept(Invocation invocation) throws Throwable {try {Object[] args = invocation.getArgs();MappedStatement ms = (MappedStatement) args[0];Object parameter = args[1];RowBounds rowBounds = (RowBounds) args[2];ResultHandler resultHandler = (ResultHandler) args[3];Executor executor = (Executor) invocation.getTarget();CacheKey cacheKey;BoundSql boundSql;//由于逻辑关系,只会进入一次if (args.length == 4) {//4 个参数时boundSql = ms.getBoundSql(parameter);cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);} else {//6 个参数时cacheKey = (CacheKey) args[4];boundSql = (BoundSql) args[5];}checkDialectExists();List resultList;//调用方法判断是否需要进行分页,如果不需要,直接返回结果if (!dialect.skip(ms, parameter, rowBounds)) {//判断是否需要进行 count 查询if (dialect.beforeCount(ms, parameter, rowBounds)) {//查询总数Long count = count(executor, ms, parameter, rowBounds, resultHandler, boundSql);//处理查询总数,返回 true 时继续分页查询,false 时直接返回if (!dialect.afterCount(count, parameter, rowBounds)) {//当查询总数为 0 时,直接返回空的结果return dialect.afterPage(new ArrayList(), parameter, rowBounds);}}resultList = ExecutorUtil.pageQuery(dialect, executor,ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);} else {//rowBounds用参数值,不使用分页插件处理时,仍然支持默认的内存分页resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);}return dialect.afterPage(resultList, parameter, rowBounds);} finally {if(dialect != null){dialect.afterAll();}}
}

复现SqlServer报错信息:

Mysql的查询时也会报limit的错

SqlServerParser,报错语句出现在这

根据源码看到会先生成这样的语句,再根据传入的number和size进行替换

SELECT TOP 9223372036854775807 user_no FROM (SELECT ROW_NUMBER() OVER (ORDER BY RAND()) PAGE_ROW_NUMBER, user_no FROM (SELECT user_no FROM dbo.[user]) AS PAGE_TABLE_ALIAS) AS PAGE_TABLE_ALIAS WHERE PAGE_ROW_NUMBER > -9223372036854775808 ORDER BY PAGE_ROW_NUMBER

上面的源码是针对SqlServer分页时的,其实打个断点跟一下就行。

演示项目地址

https://gitee.com/fireduck_admin/pagehelper-maggie-demo

总结

为什么这个问题会折腾到4点,测试环境和本地环境都没有复现,其中一个主要原因就是流量不够,可能一两个报错信息会被忽略,导致排查问题难度加大。

再加上不是测试回归的重点,以后针对上线的代码还是要加强验证。

上线到凌晨4点半 pagehelper的bug?相关推荐

  1. 剑三服务器维护是因为人太多吗,【剑网三相关 】阵营对战 从上午10点一直打到第二天凌晨6点半服务器维护 而且今天下午他们又开始打群架了...

    [楼主]2013-08-05 18:14 » 阵营对战 从上午10点一直打到第二天凌晨6点半服务器维护 而且今天下午他们又开始打群架了 剑三 服务器 华山乾坤 昨天是例行的阵营攻防战 很多人上午10点 ...

  2. 程序员吐槽职场戏精:凌晨三点半发周报,太装了!

    职场其实就是一个小小的江湖,为了在职场上生存,每个人都在极力的表现自己,甚至表演自己.但凡事适可为止,有些人表演过头就容易引起周边人的反感.一名来自互联网大厂的程序员在论坛上吐槽称,其身边的有个同事简 ...

  3. 哈佛大学凌晨4点半的景象--哈佛图书馆的二十条训言

    哈佛大学凌晨4点半的景象 哈佛图书馆的二十条训言: This moment will nap, you will have a dream; But this moment study, you wi ...

  4. 白杨SEO:跟最小徒弟聊到凌晨2点半,才发现SEO原来有这么大魅力!

    聊到凌晨2点半,我们聊了些啥? 我们晚上9点开始,这次师徒参与有七八个人,最小的徒弟就是99年的小王同学.当然,最开始就由他介绍. 从他介绍才知道,这个小徒弟自己在重庆开公司都已经大半年了,记得参加培 ...

  5. 哈佛凌晨4点半【收藏】

    哈佛校园里,不见华服,不见化妆,更不见晃里晃荡,只有匆匆的脚步,坚实地写下人生的篇章. 哈佛不是神话,哈佛只是一个证明,人的意志,精神,抱负,理想的证明. 日前,两张美国哈佛大学图书馆凌晨4点多学生仍 ...

  6. 戏精程序员凌晨三点半发周报,真的很做作!

    职场其实也是1个大大的江湖,为了在职场中求生,每个人在竭力的表现自己,甚至表演自己.但凡事适可为止,有的人创意表演过头就非常容易引发周围人的厌烦.一位来于互联网大厂的程序员在社区论坛上吐糟称,其身旁的 ...

  7. 哈佛凌晨4点半的景象

    ============喜欢这句话 [27%的人没有目标:60%的人目标模糊:10%的人有着清晰但比较短期的目标:其余3%的人有着清晰而长远的目标.] 自认为目前是 10%那种. ====== 或许这 ...

  8. 哈佛大学凌晨4点半的景象 .

    这就是差距-- 在网上看到这篇文章,觉得很有感触, 或许这再一次印证任何人每一次的成功背后都有不为人知的付出和汗水. 哈佛老师经常给学生这样的告诫:如果你想在进入社会后,在任何时候任何场合下都能得心应 ...

  9. 哈佛凌晨4点半的景象(哈佛校训)

    哈佛老师经常给学生这样的告诫:如果你想在进入社会后,在任何时候任何场合下都能得心应手并且得到应有的评价,那么你在哈佛的学习期间,就没有晒太阳的时间. 作为闻名于世的学府,哈佛大学培养了许多名人,他们中 ...

最新文章

  1. ES6深入学习记录(一)class方法相关
  2. 骑士卡:基于Kafka搭建消息中心,上亿消息推送轻松完成
  3. python3语音识别模块_语音识别(LSTM+CTC)
  4. php读取远程二进制文件,php 读取二进制文件
  5. linux+用户的shell,linux更换用户_更改linux用户登录shell的方法
  6. 手把手教你,Java如何实现二维码?【附源码】
  7. Hyper-V 的导入和导出
  8. SugarCRM 在Html中增加超连接按钮
  9. CVPR2021-PaperWithCode
  10. 大数据学习——akka自定义RPC
  11. java基础教程哪个好,面试必会
  12. Linux 正则表达式 流编辑之sed awk
  13. SQL数据库只读问题
  14. Android创建定时和周期任务
  15. 16.PHP脚本的执行
  16. 老铁的IT之路,从迷茫“愤青”到团队项目经理,他是如何一步步走出来的?!
  17. 网站搭建教程(详细步骤 )
  18. 4-2 webpack使用mapsource调试
  19. 论文整理:Probabilistic Logic Neural Networks for Reasoning
  20. 教你来使用雪碧图(CSS sprite)

热门文章

  1. [html] 写一个水平竖直居中的弹窗,带遮罩层的布局
  2. [css] 说说你对!important的理解,一般在哪些场景使用?
  3. [css] rem是如何实现自适应布局的?
  4. 前端学习(2698):重读vue电商网站19之处理图片预览操作
  5. 前端学习(2622):过滤器进行操作
  6. 前端学习(2473):创建页面组件
  7. 前端学习(2325):angular之数据修改
  8. “约见”面试官系列之常见面试题第四十一篇之VUE生命周期(建议收藏)
  9. 前端学习(988):jquery常见的api
  10. 前端学习(792):返回索引号