7.3 SpringBoot整合MyBatis分页插件github.pageHelper:实现图书列表API

news2024/9/24 15:28:05

CSDN成就一亿技术人

文章目录

  • 前言
  • 一、自己实现分页
    • 第一步,count 查询 总记录数(totalCount),计算总页数(totalPages)
    • 第二步,limit 查询 指定页数据
  • 二、不考虑分页的查询图书列表Mapper
    • BookServiceImpl
    • BookListParamBO
  • 三、集成github.pageHelper并实现分页列表
    • 第一步:引入pom依赖
    • 第二步:实现分页查询
      • BookService方法定义
      • BookServiceImpl核心实现
      • BooKBO
  • 四、封装通用分页结果
    • TgResult
    • BookListParamVO
    • application.properties
    • PostMan走一波
  • 最后


前言

在软件开发中,分页是一个非常常见的需求,无论是在Web应用程序还是在移动应用程序中,我们经常需要将大量的数据分成多个页面进行展示。
本文主要实现图书列表API,使用SpringBoot集成MyBatis分页插件github.pageHelper,首先会从「自己实现分页原理」说起,再到「使用github.pageHelper实现分页以及注意点」,最后回到图书借阅系统实战项目中,「结合通用分页结果」,实现「统一规范的图书列表API」,读完本文,你就可以轻松拿捏通用分页。


一、自己实现分页

当你可以自己造轮子以后,再去学习其它分页组件,就会觉得非常Easy!

通常分页有两种实现方式:

  1. 将全部结果通过sql查询加载到内存中,再到内存中实现分页。 缺点显而易见,一次性加载所有数据到内存中,因为结果可能会非常大,会消耗大量的资源,甚至可能导致内存溢出,所以并不推荐!
  2. 只加载当页数据,这是真正我们期望的,所以我们实现这种!

接下来,我们以Java+MySQL的「伪代码」来演示实现分页的思路!
在这里插入图片描述

第一步,count 查询 总记录数(totalCount),计算总页数(totalPages)

接收前端参数:
// 当前页码(默认第1页)
int pageNum = 1;
// 每页记录数(默认一页10条)
int pageSize = 10;

先查总计录数和总页数的目的:返回给前端展示,让用户清楚一共多少条,一共多少页

select count(*) as totalCount from book where 。。。

使用Java代码计算总页数 totalPages

int totalPages = (int) Math.ceil((double) totalCount / pageSize);

第二步,limit 查询 指定页数据

limit 接受一个或两个数字参数。
我们主要使用两个参数:第一个参数指定第一个返回记录行的偏移量offset(可选),第二个参数指定返回记录行的最大数目rows;
基本语法是:

limit [offset] rows

rows就是pageSize,所以我们需要先使用pageNum计算出offset:

if (pageNum > totalPages) {
    pageNum = totalPages;
}
int offset = (pageNum - 1) * pageSize;

然后,通过limit查询指定页数据。

select * from book where 。。。 limit offset, pageSize

二、不考虑分页的查询图书列表Mapper

有了实现的思路,在实际的开发中你会发现,所有的分页场景都是类似的逻辑,重复的代码,实现起来就是一个字:繁琐!相当的不爽,那么有没有一种让你爽的方式呢?没错,github.pageHelper组件,它可以与MyBatis等持久化框架无缝集成,帮助我们快速实现分页功能。它提供了丰富的功能和灵活的配置选项,使得分页变得非常简单。我们只需要实现【主查询SQL】,对于其它的像totalCount和totalPages等等统统不用管,这些通用功能都由它封装到内部了。

既然如此,我们就先实现不考虑分页的查询图书列表Mapper

需求:
首先,这是管理后台的图书列表,所以可以查询出所有图书。
可选的筛选条件:图书名称、图书编号、作者、图书状态(0-闲置 1-借阅中)、图书录入时间(开始时间和结束时间,YYYY-MM-DD即可)

因为是单表查询,我们仍然使用5.6 Mybatis代码生成器Mybatis Generator (MBG)实战详解 中的example方式

BookServiceImpl

对于example方式,根据需求,我们主要是构建BookExample

/**
 * 组装图书列表查询的BookExample
 **/
private BookExample buildBookPageExample(BookListParamBO paramBO) {
    BookExample example = new BookExample();
    BookExample.Criteria criteria = example.createCriteria();
    if (!StringUtils.isEmpty(paramBO.getBookName())) {
        // 图书名称不为空, 模糊查询图书名称, 等同于sql: and book_name like 'xxx%'
        criteria.andBookNameLike(paramBO.getBookName() + "%");
    }
    if (!StringUtils.isEmpty(paramBO.getBookNo())) {
        // 图书编号不为空, 模糊查询图书编号
        criteria.andBookNoLike(paramBO.getBookNo() + "%");
    }
    if (!StringUtils.isEmpty(paramBO.getAuthor())) {
        // 作者不为空, 模糊查询作者
        criteria.andAuthorLike(paramBO.getAuthor() + "%");
    }
    if (paramBO.getStatus() != null) {
        // 图书状态不为空, 指定查询该状态
        criteria.andStatusEqualTo(paramBO.getStatus());
    }
    if (paramBO.getStartDay() != null) {
        // 录入开始时间不为空, >= 录入开始时间
        criteria.andGmtCreateGreaterThanOrEqualTo(paramBO.getStartDay());
    }
    if (paramBO.getEndDay() != null) {
        // 录入结束时间不为空, <= 录入结束时间
        criteria.andGmtCreateLessThanOrEqualTo(paramBO.getEndDay());
    }
    return example;
}

解读:只是筛选条件比较多:
对于Criteria,实际就是Where条件,只是将sql转成了Java对象方式,你更喜欢哪种?文末投票看看吧~

然后,查询就更简单了,调用selectByExample即可~

/**
 *  查询图书列表
 **/
private List<Book> getBookList(BookListParamBO paramBO) {
    // 组装Example
    BookExample example = buildBookPageExample(paramBO);
    // 查询
    return bookMapper.selectByExample(example);
}

BookListParamBO

参数BookListParamBO 就是查询列表的参数BO,定义在bo包下:

@Data
public class BookListParamBO implements Serializable {
    private String bookName;
    private String bookNo;
    private String author;
    private Integer status;
    private Date startDay;
    private Date endDay;
    private int pageNum = 1;
    private int pageSize = 10;
}

三、集成github.pageHelper并实现分页列表

上段已经实现了主查询,接下来,我们使用github.pageHelper实现通用分页!

第一步:引入pom依赖

在5.1 SpringBoot整合Mybatis, 老鸟教你五分钟学会:正确且全面的方式 中曾介绍过,SpringBoot的各种starter的用途,以及官方starter和第三方框架的starter命名规范,对于pageHelper,同样如此,只需引入pagehelper-spring-boot-starter !

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.10</version>
</dependency>

因为是通用功能,所以依赖放到tg-book-common中,版本号在父项目的dependencyManagement节点指定,详见 2-1. Maven 三层项目结构搭建 和 2-2. SpringBoot API开发详解

温馨提示: 加入依赖以后,别忘了刷新Maven依赖

第二步:实现分页查询

BookService方法定义

Page<BookBO> getBookPage(BookListParamBO bookListParamBO);

Page是返回结果,这是github.pageHelper定义的通用分页结果,在下面封装通用返回结果时再映射相应字段。

BookServiceImpl核心实现

这是本文的核心,也是重点!

github.pageHelper如何通过一个主查询,就能实现的分页呢?

这里介绍我比较喜欢的使用方式:

Page<XxxBO> page = PageHelper.startPage(pgeNum, pageSize)
	.doSelectPage(() -> 你的查询方法);

不仅实现了通用分页,还非常明确且简洁。这里主要使用基础知识的是【泛型】和【Lambda表达式】,如果这两个还不熟悉的同学尽快补充一下【Java技能树】

  • PageHelper.startPage返回的是Page<E>泛型对象

在这里插入图片描述

  • doSelectPage传入的是ISelect接口参数

ISelect接口只有一个无参无返回值的方法
在这里插入图片描述
目的是调用我们的任意方法。
在这里插入图片描述
当然你可能好奇,这里是如何处理返回结果的,实际内部是使用的Mybatis拦截器Interceptor,自动实现的的count方法,组装的全部逻辑,感兴趣的同学可以查看其源码,这不是本文的重点,所以不做赘述!

OK,本文图书列表的分页的核心实现如下:

@Override
public Page<BookBO> getBookPage(BookListParamBO paramBO) {
    // 组装Example
    BookExample example = buildBookPageExample(paramBO);
    // 查询并分页
    Page<Book> page = PageHelper.startPage(paramBO.getPageNum(), paramBO.getPageSize())
            .doSelectPage(() -> bookMapper.selectByExample(example));

    // Page<Book> 转成 Page<BookBO>
    Page<BookBO> pageBO = new Page<>();
    BeanUtils.copyProperties(page, pageBO);
    for (Book book : page.getResult()) {
        BookBO bookBO = new BookBO();
        BeanUtils.copyProperties(book, bookBO);
        pageBO.add(bookBO);
    }
    return pageBO;
}

可以看到,分页的代码非常少,只是PO转BO的代码有点繁琐,不要急,后面会做单独封装,会单独写一篇详细讲解!
特别注意: 对于doSelectPage内的【Lambda表达式】请保证只调用Mapper方法,因为它只会对Mapper返回结果做Interceptor,所以如果你调用Mapper以后做了其它处理,它是无法感知到的,那也就不会生效!所以请不要那样使用!!!

BooKBO

因为筛选条件中有【图书状态】和 【录入时间】,所以在BooKBO中,也补充上这两个字段:

@Data
public class BookBO implements Serializable {
    private Integer id;
    private String bookNo;
    private String bookName;
    private Integer bookType;
    private String author;
    private String description;
    private String publisher;
    private Date publishDate;
    private String coverImage;
    // 图书状态(0-闲置 1-借阅中)
    private Integer status;
    // 录入时间
    private Date gmtCreate;
}

四、封装通用分页结果

上面实现了Service层,接下来在BookAdminController中定义API:

@PostMapping("/book/list")
public TgResult<BookBO> getBookList(@RequestBody BookListParamVO bookListParamVO) {
    return TgResult.ok(bookService.getBookPage(bookListParamVO.toBO()));
}

因为参数比较多,所以我们采用Post Body的方式,参数少的话还是推荐Get请求!

TgResult

为了支持分页,需要在通用结果 TgResult 增加分页相关返回字段,如下:

// 分页结果==========
// 当前页码
private int pageNum;
// 每页计录数
private int pageSize;
// 总数
private long totalCount;
// 总页数
private long totalPages;
// 分页数据列表
private List<T> dataList;

并增加ok重载支持分页结果Page,也就是从Page中拿出有用的字段返回结前端:

public static <T> TgResult<T> ok(Page<T> page) {
    TgResult<T> result = build(true, "200", "成功", null);
    result.setPageNum(page.getPageNum());
    result.setPageSize(page.getPageSize());
    result.setTotalCount(page.getTotal());
    result.setTotalPages(page.getPages());
    result.setDataList(page.getResult());
    return result;
}

BookListParamVO

@Data
public class BookListParamVO implements Serializable {
    private String bookName;
    private String bookNo;
    private String author;
    private Integer status;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date startDay;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date endDay;
    private int pageNum = 1;
    private int pageSize = 10;

    public BookListParamBO toBO(){
        BookListParamBO bo = new BookListParamBO();
        // 结束时间到当天的23:59:59,如果默认传入的是年月日,所以需要单独设置
        BeanUtils.copyProperties(this, bo, "endDay");
        bo.setEndDay(getDayEndTime(this.endDay));
        return bo;
    }

    /**
     * 获取指定时间的那天 23:59:59.999 的时间
     */
    private Date getDayEndTime(final Date date) {
        if (date == null) {
            return null;
        }
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime();
    }
}

主要是做了时间格式处理,保证查询的结束时间为23:59:59.999 的时间

application.properties

统一返回时间格式为 yyyy-MM-dd HH:mm:ss

spring.jackson.date-format=yyyy-MM-dd HH:mm:ss

PostMan走一波

在这里插入图片描述


最后

想要看更多实战好文章,还是给大家推荐我的实战专栏–>《基于SpringBoot+SpringCloud+Vue前后端分离项目实战》,由我和 前端狗哥 合力打造的一款专栏,可以让你从0到1快速拥有企业级规范的项目实战经验!

具体的优势、规划、技术选型都可以在《开篇》试读!

订阅专栏后可以添加我的微信,我会为每一位用户进行针对性指导!

另外,别忘了关注我:天罡gg ,发布新文不容易错过: https://blog.csdn.net/scm_2008

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/710401.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis缓存穿透击穿以及雪崩

5、Reids缓存问题与解决 5.1、背景 Redis作为一种内存性数据库&#xff0c;当查询的数据在redis缓存中时就不需要在到真实的数据库中去查&#xff0c;加快了查询速度和保护了真实数据库的安全&#xff0c;但是同时也引入了一些新的问题&#xff0c;比如查询的数据不在内存和数…

一个人也可以是【大厂】,三年程序员的生活规划心路分享!

自从工作之后&#xff0c;我就经常思考以下这些问题&#xff1a; 还有多久退休&#xff1f;明天可以退休吗&#xff1f;地球什么时候爆炸&#xff1f;我什么时候可以暴富辞职&#xff1f;我真的需要这份工作吗&#xff1f; 要问是从什么时候开始有这些问题的&#xff0c;大概…

Linux环境搭建(一)— 实现ssh连接

Linux环境搭建 一 安装虚拟机1.账号记录&#xff0c;密码root2.权限问题 二 安装ssh1.出错2.误删文件3.安装ifconfig4.安装ssh5.重装ssh6.VI环境不好用7.开放端口8.ssh文件下没有密钥文件9.无法安装ssh 三 连接ssh 写在前面&#xff1a; 使用的是VMware&#xff0c;Ubuntu环境 …

利用这个css属性,你也能轻松实现一个新手引导库

相信大家或多或少都在各种网站上使用过新手引导&#xff0c;当网站提供的功能有点复杂时&#xff0c;这是一个对新手非常友好的功能&#xff0c;可以跟随新手引导一步一步了解网站的各种功能&#xff0c;我们要做的只是点击下一步或者上一步&#xff0c;网站就能滚动到指定位置…

被中文乱码折磨的我在此总结一下编码相关知识

本文大致介绍了三个问题&#xff1a; 常见的字符编码以及他们是如何编码从而被计算机识别的&#xff1f;为什么会有这些字符编码和他们被创建的背景和顺序&#xff1f;常见的乱码问题应该如何防止以及如何解决&#xff1f; 常见的字符编码 ASCII&#xff0c;GB2312&#xff…

(小程序)按钮切换对应展示区域

(小程序)按钮切换对应展示区域 需求&#xff1a;点击按钮切换表格和图表两种展示方式 html <u-button type"primary" size"mini" text"图表" v-if"form.curType table"click"showEcharts"></u-button> <u…

同一个分支maven构建出来的包不一样?

现象 最近发布spring boot项目时遇到了一个奇怪的问题&#xff0c;日志异常信息如下&#xff1a; Caused by: java.lang.IllegalArgumentException: LoggerFactory is not a Logback LoggerContext but Logback is on the classpath. Either remove Logback or the competing …

Image Watch 的使用

目录 1、下载地址 2、安装完成 3、调试 1、下载地址 Image Watch - Visual Studio Marketplace 2、安装完成 打开VS&#xff0c;在项目->其他窗口中有image watch选项 3、调试 一直放大图像可以查看详细的色彩值&#xff0c;通道数为3

各省绿色创新效率原始数据+测算(2020-2021)

采用了考虑非期望产出的超效率SBM模型来测量中国各省的绿色创新效率。该数据包括原始数据测算结果&#xff0c;包括人力资本、资本、能源投入&#xff0c;以及技术、经济、生态效益等多个维度的数据。可以帮助研究人员了解中国各省的绿色创新效率情况&#xff0c;揭示不同省份在…

搜索引擎概念解析

搜索引擎概念解析 什么是搜索引擎 MySQL搜索引擎举例 搜索引擎是一种用于在互联网上搜索并呈现相关信息的工具。它通过自动扫描和索引大量网页内容&#xff0c;并根据用户提供的关键词或查询条件&#xff0c;返回与之相关的网页链接和摘要。 当用户在搜索引擎中输入关键词或…

系列二、RocketMQ基本概念 系统架构

一、基本概念 1.1、消息&#xff08;Message&#xff09; 消息是指&#xff0c;消息系统所传输信息的物理载体&#xff0c;生产和消费数据的最小单位&#xff0c;每条消息必须输入一个主题。 1.2、主题&#xff08;Topic&#xff09; Topic表示一类消息的集合&#xff0c;每个…

筑牢三大新型能源基础设施,能源变革的分水岭和路线图

当下面临百年未有之大变局&#xff0c;创新科技密集发生&#xff0c;面对瞬息万变的世界&#xff0c;寻找到一条通往未来的确定性道路&#xff0c;绝对是行稳致远的前置条件。 “双碳战略”持续推进&#xff0c;距离2030“碳达峰”越来越近&#xff0c;能源产业变革的重要性和迫…

【SpringMVC 学习笔记】

SpringMVC 笔记记录 1. SpringMVC 简介2. 入门案例3. 基本配置3.1 xml形式配置3.2 注解形式配置 4. 请求4.1 请求参数4.1.1 普通类型传参4.1.2 实体类类型传参4.1.3 数组和集合类型传参 4.2 类型转换器4.3 请求映射 5. 响应 1. SpringMVC 简介 三层架构 2. 入门案例 3. 基本…

如何建立自己的知识体系?202209

知识太多了&#xff0c;无法全部快速吸收进大脑&#xff0c;需要通过特定的方法、技能&#xff0c;在面对大量知识的情况下&#xff0c;快速梳理&#xff0c;构建自己的知识体系。 学习的目标&#xff0c;不仅仅是记忆知识&#xff0c;而是搜索知识、并过滤、洞察、理解、使用…

Spring Boot 整合视图层技术 Thymeleaf

大家好&#xff01;我是今越。简单记录一下在 Spring Boot 框架中如何整合 Thymeleaf 及使用。 Thymeleaf 简介 Thymeleaf 是新一代 Java 模板引擎&#xff0c;它类似于 Velocity、FreeMarker 等传统 Java 模板引擎&#xff0c;但是与传统 Java 模板引擎不同的是&#xff0c;T…

爆肝整理,App测试小技巧,全覆盖功能到性能测试...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 app测试是使用手动…

ES查询[全网最全免费送付费内容]

ES查询 相关度搜索&#xff0c;需要计算评分 _score 相关度评分用于对搜索结果排序&#xff0c;评分越高则认为其结果和搜索的预期值相关度越高&#xff0c;即越符合搜索预期值。在7.x之前相关度评分默认使用TF/IDF算法计算而来&#xff0c;7.x之后默认为BM25。 源数据&…

Win 10 重装系统(PE方式)

前言&#xff1a; 最近这个笔记本&#xff08;ThinkPad E480&#xff0c;使用了四年左右&#xff09;用起来很卡&#xff0c;经常开机状态时&#xff0c;合上之后&#xff0c;再打开屏幕就卡死了&#xff0c;鼠标和键盘按了都没有反应&#xff0c;无奈之下只能强制按电源关机后…

SpringCloud——Nacos下载

文章目录 nacos简介nacos下载nacos的启动访问nacos nacos简介 Nacos&#xff08;全称为 “Naming and Configuration Service”&#xff09;是阿里巴巴开源的一个用于实现动态服务发现、服务配置和服务元数据管理的项目。它是一个分布式系统的服务基础设施&#xff0c;为云原生…

turtle画春联

import turtle #右边春联 turtle.penup() turtle.goto(100,150) turtle.pendown() turtle.color(red,red) turtle.begin_fill() turtle.forward(50) turtle.right(90) turtle.forward(400) turtle.right(90) turtle.forward(50) turtle.right(90) turtle.forward(400) turtle.e…