Lecture07-分页查询

文章目录

    [toc]

    单词词性音标含义
    Pagen./peɪdʒ/页(Page 对象封装页码、每页条数、总条数等分页信息)
    Paginationn./ˌpædʒɪˈneɪʃn/分页(PaginationInnerInterceptor 分页插件,自动给 SQL 加 LIMIT)
    Interceptorn./ˌɪntəˈseptə(r)/拦截器(在 SQL 执行前进行拦截和改写的组件)
    Totaln./ˈtəʊtl/总数(getTotal() 获取满足条件的总记录数)
    Recordsn./ˈrekɔːdz/记录(getRecords() 获取当前页的数据列表)

    Lecture07-分页查询

    一、为什么需要分页

    数据多的时候不能一次性全查出来(慢、占内存、页面也显示不下),需要「一页一页地查」。SQL 层面分页就是 LIMIT:

    SELECT ... FROM t_emp LIMIT 0, 3;   -- 第 1 页,每页 3 条
    SELECT ... FROM t_emp LIMIT 3, 3;   -- 第 2 页,每页 3 条
    

    MyBatis-Plus 不用我们写 LIMIT:配置好分页插件,调用 selectPage 方法即可。


    二、配置分页插件(一步配置,全局生效)

    1、额外增加依赖

    <!-- 分页插件必须单独引入 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-jsqlparser</artifactId>
        <version>3.5.17</version>
    </dependency>
    

    2、配置

    • 配置类和 @Bean 注解仍然还是 Spring 提供的功能,不是 MybatisPlus
    • 配置类和 @Bean 注解是为了把 MybatisPlusInterceptor 的对象放入 IoC 容器
    • MybatisPlusInterceptor 是 MybatisPlus 开发的拦截器 注意:这不是 SpringMVC 的拦截器而是 Mybatis 的机制,Mybatis 通过拦截器让使用者可以定制框架的功能
    package com.atguigu.mp.config;
    
    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MybatisPlusConfig {
    
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    
            // 创建分页插件对象
            PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
    
            // 开启功能:把超出合理范围的页码修正到第一页
            paginationInnerInterceptor.setOverflow(true);
    
            // 添加分页插件,指定数据库类型为 MySQL
            interceptor.addInnerInterceptor(paginationInnerInterceptor);
            return interceptor;
        }
    
    }
    

    第 10 章的乐观锁插件也是往这个拦截器里加,到时候再添一行即可。


    三、基本用法:selectPage

    1、代码

    @Test
    public void testSelectPage() {
        // 第 1 页,每页 3 条
        Page<Employee> page = new Page<>(1, 3);
    
        // 分页查询:参数 1 是 Page 对象,参数 2 是查询条件(不带条件传 null)
        Page<Employee> result = employeeMapper.selectPage(page, null);
    
        // 当前页数据
        List<Employee> records = result.getRecords();
        records.forEach(System.out::println);
    
        // 分页信息
        System.out.println("当前页数据条数 = " + records.size());
        System.out.println("总记录数 total = " + result.getTotal());
        System.out.println("总页数 pages = " + result.getPages());
        System.out.println("当前页码 current = " + result.getCurrent());
        System.out.println("页容量 size = " + result.getSize());
        System.out.println("是否有下一页 hasNext = " + result.hasNext());
    }
    

    2、控制台日志(重点观察两段 SQL)

    ==>  Preparing: SELECT COUNT(*) AS total FROM t_emp
    ==> Parameters:
    <==    Columns: total
    <==        Row: 4
    
    ==>  Preparing: SELECT emp_id,emp_no,emp_name,emp_salary,emp_birthday,phone_num,dept_id FROM t_emp LIMIT 0,3
    ==> Parameters:
    <==    Columns: emp_id, emp_no, emp_name, emp_salary, emp_birthday, phone_num, dept_id
    <==        Row: 1, No.10001, 张三, 8000.00, ...
    <==        Row: 2, No.10002, 李四, 12000.00, ...
    <==        Row: 3, No.10003, 王五, 6000.00, ...
    

    分页插件帮我们自动执行了两段 SQL:

    1. COUNT(*):统计满足条件的数据总条数,也就是总记录数
    2. LIMIT:查询当前页数据

    四、图解:分页执行流程

    分页查询 selectPage 的执行流程

    流程要点:

    1. 我们传入 Page(1, 3) 和条件
    2. 分页插件拦截 SQL 执行,拆成 count 查询和数据查询两段
    3. 两段结果组装进同一个 Page 对象返回

    五、分页 + 条件构造器

    分页和条件查询天然配合,条件是 QueryWrapper / LambdaQueryWrapper 都行:

    @Test
    public void testSelectPageWithCondition() {
        // 分页:第 2 页,每页 2 条
        Page<Employee> page = new Page<>(2, 2);
    
        // 条件:查询部门 1 的员工,按工资降序
        LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Employee::getDeptId, 1)
               .orderByDesc(Employee::getEmpSalary);
    
        Page<Employee> result = employeeMapper.selectPage(page, wrapper);
    
        System.out.println("total = " + result.getTotal());
        System.out.println("pages = " + result.getPages());
        result.getRecords().forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT COUNT(*) AS total FROM t_emp WHERE dept_id=?
    ==> Preparing: SELECT emp_id,... FROM t_emp WHERE dept_id=? ORDER BY emp_salary DESC LIMIT 2,2
    

    注意:条件(WHERE、ORDER BY)在 count 查询和数据查询中都会带上,结果一致。


    六、分页对象常用方法

    方法含义
    getRecords()当前页数据列表
    getTotal()总记录数(count 查询结果)
    getPages()总页数(框架自动计算)
    getCurrent()当前页码
    getSize()每页条数
    hasNext()是否有下一页
    hasPrevious()是否有上一页

    七、常见问题

    问题原因与解决
    分页不生效,LIMIT 没出现在 SQL 里,查出来还是全部忘了配置 MybatisPlusInterceptor + PaginationInnerInterceptor,或配置类没被扫描到
    Page 导包报错用 com.baomidou.mybatisplus.extension.plugins.pagination.Page
    数据量大时 count 查询慢项目后期可优化:数量级小时先不管;必要时使用 Page(long current, long size, boolean searchCount) 关闭 count 查询

    八、本章小结

    • 三步使用:配置分页插件 → new Page(页码, 每页条数) → 调用 selectPage
    • 插件自动执行 count + limit 两段 SQL,组装成 Page 返回
    • 分页与条件构造器(LambdaQueryWrapper)天然配合
    • 忘记配置插件是分页失效最常见的原因

    下一章学习更新操作的进阶用法:UpdateWrapper 局部更新。

    end