Mybatis-plus 分页查询的“坑”(不,是我不会用)

经常做列表界面性能优化的小伙伴都遇到过这样的情况,索引什么的都命中,但是性能就是不好,其中一种情况就是因为 列表为分页查询,列表查询本身挺快的,但是拿到分页的count语句在数据库中执行慢的一批。

count慢,最直接的解决办法我觉得就是不查询总条数,用户一般不会关心具体有多少页,更多时候进入界面响应要快,然后输入查询条件,过滤掉大部分数据。像百度,谷歌一样不需要展示具体页数和总条数,然而,上面我说的这些,没有打动产品经理,也没时间所有界面挨个修改,那目前就只能继续处理数量查询。

今天其他的情况先不说,先吐槽一下mybatis-plus (下面全部简写为mp)自带的分页工具的小坑。测试中偶尔会出现count 语句包裹整个sql的情况,比如

select count(*) from (

        select id, name, createTime, otherKeys  from tableA  a

                join tableR  rr on a.id = rr.a_id

                where a.is_deleted = 0 order by create_time desc

) as total

这个sql能正常运行,但是处于性能考虑,我们可以去掉 order,去掉select 中的字段

select count(*)  from tableA  a

                join tableR  rr on a.id = rr.a_id

                where a.is_deleted = 0

这样性能不是更好吗?

代码中直接屏蔽count查询,再手写count语句,

//方法一
Page page = new Page(1, 10);
page.setSearchCount(false);
//手写count语句


//方法二
//或者mapper中写一个同名查询以 _COUNT结尾,然后这样也行
page.setCountId("selectAll_COUNT");

自定义count语句时,如果sq中的join太多,还大多只是拿来查询冗余数据,就可以尝试选择把一些不影响数量的left join 表给去掉,如果left join的表中的字段参与了界面上的查询,那就用  <if> </if> 标签判断,有这个查询条件时才参与 join。有些inner join 确保业务正常的情况下也能去掉,比如一定是一对一且一定不为空,只是作为查询展示字段不参与其他表的join,这样的 inner join 根据业务具体情况看是否也能去掉。

这样的好处是:大部分正常用户进来都是输入搜索条件查询,首页的数据就要尽量的快。有了搜索条件后,能过滤掉大部分数据,这样再把表join进来对性能影响不大。

接下来就是翻了一下源码,想知道到底哪些情况下回出现这样的坑,一不小心有发现了框架的“bug“, bug我打了引号,后面解释。

我的问题版本是

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

mp的默认实现是:

PaginationInnerInterceptor,当mp判断count sql 无法优化时会调用这个方法拼接sql

然后整理下哪些情况会调用这个方法

按图中的标记,以下情况会不优化 count 语句

1:page对象中 optimizeCountSql 设置为false。(可以手动修改)自己设置的放弃优化

2:后面单独写。。。。

3:sql是用union 把多个表合并的不行

4:select 中存在  #{} ${} 参数的不优化,因为可能你的参数是一个函数,比如max 这样就只有一行数据,count语句会不正确

5:包含 distinct、groupBy不优化,这种不解释了

6:前面如果遇到了异常,这是作为兜底的情况

一个bug!如下

然后,根据第4个条件,我突然想到,如果我在sql中就真的直接写一个max函数,这个不属于其中任何一种不优化情况,回会不会有问题呢?于是直接把项目的一个sql复制一下,写了mapper做测试,具体代码就不贴出来了,很简单  select max(id) from table_name

分页查询的结果是:

看到了吧,total = 3,但是只有一个records !

本来我想去github提一个 issues, 界面都打开了,想了一下又觉得这不算一个真正的bug吧。因为,sql的select用了这样的函数,然后再去分页查询,这种本来就是不正确的使用方式,兼容了最好,报错提示开发者也行。不处理,也能接受吧,程序员自己使用错误在先。这就是为什么前面 bug 这个词要打引号的原因。

还没完,上面列的6个条件。其中第二个的作用是把sql字符串解析成java对象才,方便操作。

第6条写的异常情况,我自己遇到的也就是这里!我有一个sql,不符合上面任何一个不优化的规则,但是还是出了问题,我去掉了里面的join,where等,把它简化到极致如下:

select 1 from tableA rr

凭借直觉和理智,这个没有不优化的理由,我看到的sql应该是 select count(*) from tableA

然后,把这个sql放入条件2中的代码执行一下,

Select select = (Select) CCJSqlParserUtil.parse("select 1 from tableA rr");

然后就报错了!!报错后就执行了条件6,因此没有优化这个sql的count语句

我试了 根据错误提示,我把 rr 换成as 也会报错,其他的随便试了几个都没报错!所以我认为 rr 是被识别成了一个关键字吧。CCJSqlParserUtil 里面的代码很多字符串解析的,看起来很痛苦,如同汇编,哪位大佬梳理了具体原因麻烦教小弟一下啊。。。

最后,PageHelper 我没有测试,因为这个出了很多次问题,已经被我们禁用了,

1:如果开启的PageHelper 但是程序异常或者有判断条件跳过sql执行,导致PageHelper没有执行也没有清理,会导tomcat致线程池参数混,界面数据不对,遇到过两次,然后全局排查

2:我们项目自定义了一些sql拼装的拦截器,用PageHelper会导致有些情况下拼装的sql不正确,所以就放弃了PageHelper

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MyBatis-Plus 是一个 MyBatis 的增强工具包,提供了很多方便的功能和特性,其中包括分页查询插件。使用 MyBatis-Plus分页查询插件可以简化分页查询的操作。 在 MyBatis-Plus 中,分页查询可以通过 `Page` 对象和 `PageHelper` 工具类来实现。下面是使用 MyBatis-Plus 进行分页查询的示例代码: 1. 首先,添加 MyBatis-Plus 和分页插件的依赖到你的项目中。你可以在项目的 pom.xml 文件中添加以下依赖: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>最新版本</version> </dependency> ``` 2. 创建一个 `Page` 对象,并设置分页参数: ```java // 创建一个 Page 对象 Page<User> page = new Page<>(current, size); // 设置分页参数 page.setPages(current); // 当前页码 page.setSize(size); // 每页显示的记录数 ``` 3. 在 MyBatis 的 Mapper 接口中使用 `@Param` 注解传递 `Page` 对象,并在 SQL 中使用 MyBatis-Plus 提供的分页查询方法: ```java @Mapper public interface UserMapper extends BaseMapper<User> { List<User> selectUserPage(@Param("page") Page<User> page, @Param("name") String name); } ``` 4. 在 Service 层中调用分页查询方法: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public IPage<User> getUserPage(long current, long size, String name) { Page<User> page = new Page<>(current, size); return userMapper.selectUserPage(page, name); } } ``` 这样,你就可以使用 MyBatis-Plus分页查询插件进行分页查询了。记得在你的 SQL 语句中使用 MyBatis-Plus 提供的分页查询方法,而不是传统的 LIMIT 语句。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值