Mybaits-PageHelper:有些SQL没使用PageHelper却莫名其妙加上了limit ?

本次记录一次工作中偶然发现的bug,在测试环境跑流程的时候偶然发现有时候接口莫名其妙报错,刷新一下又好了。拉日志发现SQL语句上莫名其妙的拼接上了limit ? ,网上百度半天都说是开启了PageHelper,实际上报错的SQL没有启用PageHelper,研究源码才发现问题

问题日志截图:

发生问题的代码片段(公司代码要保密,怕被逮住了):

可以看出压根没使用PageHelper,但是网上搜半天找不到原因,但项目里确实是使用了PageHelper,且这个现象很像,故顺着日志往上捞,逮住这个线程看看【io-33003-exec-8】

发现在报错之前,这个线程干了外一个任务,有个日志输出,看看输出了啥

(选项目,用户角色标识,1-工程实施经理,2-施工,3-监理,4-设计。当前:0)

既然本次一进来就报错,就瞅瞅这个该死的线程上次干啥去了,是不是被玩坏了,找到上次执行的代码

这个时候发现,前人挖坑,开启了PageMethod.startPage,但是实际上没有执行任何SQL,就return跑了!!!

所以后来这个线程执行完任务就被线程池回收,又分配了新的任务。

可是为啥回收了却没回收干净能影响下个任务呢?

大家应该都能想到ThreadLoacl(ThreadLoacl内存泄漏 经典面试题属于是)

线程池中的线程是没有被销毁的,线程用完后又要回收到线程池中的。如果一个线程不销毁,那么跟随这个线程的ThreadLocalMap就一直存在,上次变量的变更,下次依然在上面使用。

可是本次问题好像和ThreadLocal无关欸?

让我们瞅瞅PageHelper

贴源码:

PageMethod.startPage方法:

注意到startPage方法最后是调用了setLocalPage

setLocalPage方法:

可以看出最后是对ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();进行了设置

所以导致该线程本次执行的任务却拿到了上次任务的参数

可是又有问题了,那正常使用PageHelper为啥不会出现这些问题呢?为什么刷新一下又不报错了呢?

顺着源码看下去,会发现在PageHelper后续执行,在mybaits拼接参数执行完SQL后(无论本次SQL会不会正常运行还是报错),会执行clearPage这个方法(因为afterAll这个方法在trycatch执行SQL的finally中了)

clearPage:

所以在PageMethod.startPage后,下一次执行的SQL会被影响到,即使本次请求已经结束,但是存在ThreadLoacl中的参数,还是会影响线程复用执行的下一次请求。

解决方法也有三种:

(1)PageMethod.startPage后面紧紧跟随sql语句,保证每个startPage方法都执行了sql语句,执行完sql语句后PageHelper会调用clearPage方法清除theardLocal里的参数(此处只能保证你写的,你永远也不知道其他人给你埋了什么坑,本次发现问题实际上已经上线生产大半年了)

(2)每次执行方法或后,都调用clearPage方法,但是过于麻烦 

(3)在拦截器中调用PageMethod.clearPage(推荐)(若有AOP实现日志处理,也可塞到AOP中处理)

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis-Plus 是一个 MyBatis 的增强工具,它提供了许多功能,其中之一就是简化 SQL 的编写。下面演示一些 MyBatis-Plus 简化 SQL 的例子: 1. 查询条件构造器 使用 QueryWrapper 可以方便地构造查询条件,例如: ``` QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name", "Tom").ge("age", 18).orderByDesc("id"); List<User> userList = userMapper.selectList(wrapper); ``` 这个例子中,我们使用 QueryWrapper 来构造查询条件,其中 eq 表示等于,ge 表示大于等于,orderByDesc 表示降序排序。最后使用 selectList 方法查询符合条件的数据。 2. 分页查询 使用 Page 对象可以轻松实现分页查询,例如: ``` Page<User> page = new Page<>(1, 10); IPage<User> userPage = userMapper.selectPage(page, null); List<User> userList = userPage.getRecords(); ``` 这个例子中,我们创建了一个 Page 对象,指定查询第一页的 10 条数据。然后使用 selectPage 方法查询符合条件的数据,并将结果封装在一个 IPage 对象中。最后从 IPage 对象中获取查询结果。 3. Lambda 表达式 使用 Lambda 表达式可以更加简洁地构造查询条件,例如: ``` List<User> userList = userMapper.selectList(Wrappers.<User>lambdaQuery() .eq(User::getName, "Tom") .ge(User::getAge, 18) .orderByDesc(User::getId)); ``` 这个例子中,我们使用 Wrappers.lambdaQuery() 创建一个 LambdaQueryWrapper 对象,然后使用 eq 方法指定查询条件,ge 方法指定大于等于条件,orderByDesc 指定降序排序。最后使用 selectList 方法查询符合条件的数据。 以上是 MyBatis-Plus 简化 SQL 的几个例子,这些功能可以大大简化 SQL 的编写,提高开发效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值