Spring Boot项目如何使用MyBatis实现分页查询

写在前面:大家好!我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正,感谢大家的不吝赐教。我的唯一博客更新地址是:https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油,冲鸭!

用知识改变命运,用知识成就未来!加油 (ง •̀o•́)ง (ง •̀o•́)ง

文章目录

为什么需要分页查询

分页查询是一种常见的数据库查询技术,用于将查询结果分成多个页面展示,而不是一次性返回所有数据。使用分页查询主要是为了减少数据库压力、减少网络传输数据量、提高系统的稳定性、提高客户体验

减少数据库压力

一次性查询全部数据(例如百万条记录)会占用大量的资源(CPU、内存、I/O),导致响应变慢甚至系统崩溃。分页后,每次仅查询少量数据(如每页100 条),可以显著降低负载。

减少网络传输数据量

分页查询每次只传输当前页的数据,相比于全表查询会极大的减少网络传输的数据量,降低网络带宽的占用。

提高系统的稳定性

后端服务处理分页查询时,单次处理的数据量可控,避免因一次性加载大数据导致内存耗尽出现 OOM 问题。对于前端也由于无需一次性渲染大量的数据而减少了内存崩溃的风险。

提升用户体验

分页查询由于单次查询的数据量少,后端与前端可以快速的处理相关的数据。用户无需进行长时间的等待,极大的提高了客户的体验。

原始的实现方式

如果不使用分页查询相关的插件需要我们自己计算分页查询的偏移量offset ,还需要手动查询结果集以及数据总条数,并且在相关的 Mapper.xml 中定义分页查询的 SQL 语句。主要实现步骤如下:

计算偏移量

手动分页查询需要我们通过在 SQL 语句中添加分页相关的语法来实现,例如在 MySQL 中的语法:

sql 复制代码
SELECT * FROM users LIMIT #{pageSize} offset #{offset};

其中,#{offset} 表示偏移量,但是前端的分页查询请求中一般只有查询第几页 page 和每页的大小 pageSize。需要我们先计算一下偏移量是多少。

需要注意前端传的 page 是从 0 开始的还是从 1 开始的。

  • 0 开始则 offset = page * pageSize
  • 1 开始则 offset = (page - 1) * pageSize

在Mapper接口中定义查询方法

java 复制代码
/**
 * 分页查询结果集
 * @param page
 * @param offset
 * @param name
 * @param categoryId
 * @param status
 * @return
 */
List<DishVO> pageQuery(int page, int offset, String name, Integer categoryId, Integer status);

/**
 * 查询总条数
 * @return
 */
int getTotalSize();

编写SQL语句

sql 复制代码
<mapper namespace="com.sky.mapper.DishMapper">

    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into dish (name, category_id, price, image, description, status, create_time, update_time, create_user, update_user)
        values (#{name}, #{categoryId}, #{price}, #{image}, #{description},#{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})
    </insert>
    
    <select id="pageQuery" resultType="com.sky.vo.DishVO">
        select d.*, c.name as categoryName from dish d left outer join category c on d.category_id = c.id
        <where>
            <if test="name != null">
                and d.name like concat('%', #{name}, '%')
            </if>
            <if test="categoryId != null">
                and d.category_id = #{categoryId}
            </if>
            <if test="status != null">
                and d.status = #{status}
            </if>
        </where>
        order by d.create_time desc
        limit #{page}
        offset #{offset}
    </select>
    
    <select id="getTotalSize" resultType="java.lang.Integer">
        select count(*) from dish;
    </select>
</mapper>

开发流程及完整代码

在开发过程中 由外而内 进行开发效率会更高一些,我们一般不需要 先写 Mapper.xml 中的 SQL语句 ,再定义Mapper接口 中的方法,然后再通过 Service类 中进行调用。

一般会从 Service类 开始写起,然后再通过编辑器的快捷方式帮助我们生成相关的代码,再一层一层的实现。

Controller层

java 复制代码
@GetMapping("/page")
@ApiOperation("菜品分页查询")
public Result<PageResult> pageQuery(DishPageQueryDTO dishPageQueryDTO) {
    log.info("菜品分页查询开始[{}]", dishPageQueryDTO);
    return dishService.pageQuery(dishPageQueryDTO);
}

Service实现类

java 复制代码
@Override
public Result<PageResult> pageQuery(DishPageQueryDTO dishPageQueryDTO) {

    // 计算偏移量
    int offset = (dishPageQueryDTO.getPage() - 1) * dishPageQueryDTO.getPageSize();
    // 查询当前页的数据
    List<DishVO> dishVOList = dishMapper.pageQuery(dishPageQueryDTO.getPageSize(), offset, dishPageQueryDTO.getName(),
            dishPageQueryDTO.getCategoryId(), dishPageQueryDTO.getStatus());
    // 查询数据库中的总条数
    int total = dishMapper.getTotalSize();
    PageResult pageResult = new PageResult();
    pageResult.setTotal(total);
    pageResult.setRecords(dishVOList);
    log.info("分页查询结果为[{}]", pageResult);
    return Result.success(pageResult);
}

Mapper接口方法

java 复制代码
/**
 * 分页查询结果集
 * @param page
 * @param offset
 * @param name
 * @param categoryId
 * @param status
 * @return
 */
List<DishVO> pageQuery(int page, int offset, String name, Integer categoryId, Integer status);

/**
 * 查询总条数
 * @return
 */
int getTotalSize();

xml文件SQL

sql 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.DishMapper">

    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into dish (name, category_id, price, image, description, status, create_time, update_time, create_user, update_user)
        values (#{name}, #{categoryId}, #{price}, #{image}, #{description},#{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})
    </insert>
    
    <select id="pageQuery" resultType="com.sky.vo.DishVO">
        select d.*, c.name as categoryName from dish d left outer join category c on d.category_id = c.id
        <where>
            <if test="name != null">
                and d.name like concat('%', #{name}, '%')
            </if>
            <if test="categoryId != null">
                and d.category_id = #{categoryId}
            </if>
            <if test="status != null">
                and d.status = #{status}
            </if>
        </where>
        order by d.create_time desc
        limit #{page}
        offset #{offset}
    </select>
    
    <select id="getTotalSize" resultType="java.lang.Integer">
        select count(*) from dish;
    </select>
</mapper>

使用插件实现

使用分页插件可以极大的简化分页查询实现。虽然实现的主要原理还是通过原始实现方式中提到的逻辑,但是通过分页插件我们就可以少写很多代码而且一般分页插件(例如 PageHelper ) 会通过动态 SQL 的构建和优化,能够有效避免传统分页方法的性能问题。

使用插件进行分页查询只需要修改一下上述原始实现方式的Service类xml 文件中的 SQL 语句即可。

引入分页插件依赖

xml 复制代码
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>

Service实现类

java 复制代码
@Override
public Result<PageResult> pageQuery(DishPageQueryDTO dishPageQueryDTO) {

    PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());
    Page<DishVO> page = dishMapper.pageHelperQuery(dishPageQueryDTO);

    PageResult pageResult = new PageResult();
    pageResult.setTotal(page.getTotal());
    pageResult.setRecords(page.getResult());
    log.info("分页查询结果为[{}]", pageResult);
    return Result.success(pageResult);

}

xml文件SQL

xml 复制代码
<select id="pageHelperQuery" resultType="com.sky.vo.DishVO">
    select d.*, c.name as categoryName from dish d left outer join category c on d.category_id = c.id
    <where>
        <if test="name != null">
            and d.name like concat('%', #{name}, '%')
        </if>
        <if test="categoryId != null">
            and d.category_id = #{categoryId}
        </if>
        <if test="status != null">
            and d.status = #{status}
        </if>
    </where>
    order by d.create_time desc
</select>

与原始分页的不同

使用分页插件进行分页不需要手动计算分页查询的偏移量 ,在写SQL语句时也不需要显式地使用 LIMITOFFSET 来实现分页。而且分页插件也会直接将查询的结果集总条数 封装到 Page对象 中,不需要我们手动的查询结果集和总条数。


  1. mysql开启缓存、设置缓存大小、缓存过期机制
  2. PageHelper分页插件最新源码解读及使用
  3. 苍穹外卖
相关推荐
加油,旭杏7 分钟前
【go语言】grpc 快速入门
开发语言·后端·golang
brzhang27 分钟前
墙裂推荐一个在 Apple Silicon 上创建和管理虚拟机的轻量级开源工具:lume
前端·后端
沈韶珺2 小时前
Visual Basic语言的云计算
开发语言·后端·golang
沈韶珺2 小时前
Perl语言的函数实现
开发语言·后端·golang
美味小鱼2 小时前
Rust 所有权特性详解
开发语言·后端·rust
我的K84092 小时前
Spring Boot基本项目结构
java·spring boot·后端
字节全栈_kYu2 小时前
FastDFS实用笔记 (Docker 搭建环境 + 整合 SpringBoot)
spring boot·笔记·docker
慕璃嫣3 小时前
Haskell语言的多线程编程
开发语言·后端·golang
Hello.Reader7 小时前
深入浅出 Rust 的强大 match 表达式
开发语言·后端·rust