Mybatis-Pagehelper参数supportMethodsArguments引起的血案

0x00 背景

一个历史悠久的项目,使用的技术栈主要是 spring cloud 体系,属于 service 范畴,不给外部提供接口,但是集成了 myabtis-pagehelper,具体的版本如下:

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.6</version>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.1</version>
</dependency>

相关的配置:

pagehelper:
  helperDialect: mysql
  reasonable: true
  supportMethodsArguments: true
  params: count=countSql

0x01 现象描述

同事在这个项目中新增加了一个总能,属于定时执行的,循环取数据,每次取 1000 条,但是上线后发现第一次能正常查询出数据,但是第二次往后就查询不出数据了,但是数据库中是有数据的。SQL 如下:

-- sql 已经做简化处理
select
    *
from ${tableName}
where type = #{type,jdbcType=VARCHAR}
    and time >= #{time,jdbcType=TIMESTAMP}
    <if test="pageSize != null and pageSize != '' and pageNum!=null and pageNum!=''">
        limit #{pageNum}, #{pageSize}
    </if>

0x02 问题分析

刚刚开始以为是 sql 和数据的问题,一顿分析,将 sql 的参数参数打印出来后,用工具查询发现是能查询出数据的。由此判断不是参数以及sql 的问题,于是添加 mybatis 的参数将 sql 打印出来。

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

查看查询的过程, 发现每一次查询会先执行一个 count 操作,查询总数,总数查询完成后,再去执行查询操作。第一次 count 是有数据,但是第二次查询 count 查询的总数为 0,导致也就不执行查询操作了。看完查询过程后,同事说他这个不是分页,我有个疑问为啥会执行分页操作呢,但是也没多想。继续排查对比。

经过对比前后两次的 count 的 sql 发现,第二次的 count 的 sql 多了一个 limit,而此时的 pageNum 和 pageSize 变成了 1,1。 count 的结果总列数就是 1,limit 1, 1 后肯定是没有结果的。

在这里插入图片描述

0x03 问题解决

发现这个情况后,前面说过他应该不走分页逻辑,应该是正常自定义 limit,也就是说不应该会前置执行 count 计数查询,而应该直接去查询数据。猜想是 pageNum 和 pageSize 两个参数导致的,将参数改名后发现正常执行查询而不执行 count 操作了。

那么究竟是为啥会这样呢,经过查看源码,发现在 PageParams 中有如下的逻辑:

} else if (parameterObject instanceof IPage || supportMethodsArguments) {
                try {
                    page = PageObjectUtil.getPageFromObject(parameterObject, false);
                } catch (Exception e) {
                    return null;
                }
            }

如果参数实现了 IPage 接口或者 supportMethodsArguments 为 true 就会从参数中获取 pageNum 和 pageSize 参数,也就是当 supportMethodsArguments 为 true 时并且参数中有 pageNum 和 pageSize 参数时,会执行分页的相关逻辑。

0x04 总结

总的来说,supportMethodsArguments 为 true 时需要特别注意,传递的参数中不能有 pageNum 和 pageSize 参数,否则会执行分页逻辑。

带着好奇心去看了一下 pagehepler 的源码,发现某些特殊的 sql 走分页逻辑时,会重写成子查询,比如:

select so.id,so.address,so.area_code,so.area_id,so.del_flag,so.email,so.fax,so.grade,so.icon,so.master, so.name,so.parent_id,so.parent_ids,so.phone,so.remarks,so.type,so.zip_code from sys_organization so LEFT JOIN sys_user_organization suo ON (suo.org_id = so.id or FIND_IN_SET(suo.org_id,so.parent_ids)) where suo.user_id = ? group by so.id LIMIT ? "

生成的 count 语句是:

SELECT count(0) FROM (SELECT so.id, so.address, so.area_code, so.area_id, so.del_flag, so.email, so.fax, so.grade, so.icon, so.master, so.name, so.parent_id, so.parent_ids, so.phone, so.remarks, so.type, so.zip_code FROM sys_organization so LEFT JOIN sys_user_organization suo ON (suo.org_id = so.id OR FIND_IN_SET(suo.org_id, so.parent_ids)) WHERE suo.user_id = ? GROUP BY so.id LIMIT ?) table_count

查看了一下源码是有特殊的判断,我们遇到的那个 sql 不满条件,所以只是简单的重写了 sql,没有重写成子查询。

因为是 count 语句的话其实不应该有 limit,有 limit 的话多多少少有点问题。也尝试修复了一下,虽然能重写成子查询,虽然查询 count 正常了,后续正常的查询就不正常(需要处理 sql 的参数,看逻辑后续会还拼接 limit 导致语法错误,想想也没有啥意义(虽然带了 limit但是可能也不是分页),所以还是放弃了。后续关注下,看看作者大大有没有更好的解决办法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值