基于 MyBatis Plus 分页封装分页方法

news2024/12/28 10:27:49

一、前言

作为一个 CRUD 工程师,查询必然少不了,分页查询更是常见,市面上也有很多成熟的分页插件,都各有优缺点,这里整理一下,基于 MybatisPlus 的分页插件进一步封装分页的公共方法。

二、对象封装

其实分页插件已经提供了很强大的功能,但是在业务开发的时候不够精简,返回了很多我们并不关注的数据,在这个基础上进一步封装,使其更贴合我们的业务开发。

2.1 分页结果对象封装

首先我们定义一个通用的分页结果对象,PageVO 包含我们关注的主要几个数据值,总条数,总页数,数据集。这里为了兼容各种数据类型,这里的数据集的类型通过泛型指定

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageVO<V> implements Serializable {
    private static final long serialVersionUID = 1L;

    @Schema(description = "总条数")
    private Long total;
    @Schema(description = "总页数")
    private Long pages;
    @Schema(description = "数据")
    private List<V> records;

}

2.2 分页查询对象封装

为了兼容查询对象的不同类型,这里使用泛型定义查询对象类型,后面我们只需要根据使用场景定义对应的 Query 对象就可以了

@Data
public class PageQuery<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    @Schema(description = "当前页码", defaultValue = "1")
    private Integer pageNum = 1;
    @Schema(description = "每页显示条数", defaultValue = "10")
    private Integer pageSize = 10;
    @Schema(description = "排序对象,支持多字段排序")
    private List<OrderItem> orderItems;
    @Schema(description = "查询对象")
    private T search;
}

2.3 结合 Query 对象使用案例

第一步:

比如我们现在要完成用户列表的分页查询,那么首先我们需要定义对应的查询对象 **UserQuery, **这里简单展示通过用户名和昵称进行查询。

@Data
public class UserQuery implements Serializable {
    private static final long serialVersionUID = 1L;

    @Schema(description = "用户名")
    private String username;

    @Schema(description = "昵称")
    private String nickname;
}

第二步:

定义我们返回时需要的结果对象,我这里就叫 **UserListVO **我习惯将列表的 **VO **对象命名为 **xxxListVO,**详情对象命名为 xxxDetailVO

@Data
public class UserListVO implements Serializable {
    private static final long serialVersionUID = 1L;

    @Schema(description = "主键ID")
    private Long userId;

    @Schema(description = "用户名")
    private String username;

    @Schema(description = "昵称")
    private String nickname;

    @Schema(description = "创建时间")
    private LocalDateTime createTime;

    @Schema(description = "更新时间")
    private LocalDateTime updateTime;
}

第三步:

controller 层编写接口

@Operation(summary = "分页查询")
@PostMapping("/page")
public R<PageVO<UserListVO>> findPage(@RequestBody PageQuery<UserQuery> userQuery) {
    PageVO<UserListVO> page = userService.findPage(userQuery);
    return R.ok(page);
}

可以看到这里我们通过前面定义的公共对象,以及具体的业务对象,经过简单的组装完成了,请求参数 **userQuery **以及返会结果的封装,并且我们可以很清楚的知道对应的类型,想要扩展也很容易实现,以后所有的分页查询基本上都是类似的格式,不同的在于我们根据不同使用场景封装对应的业务返回 xxxVO 以及查询对象 xxxQuery

第四步:

具体的分页查询实现,即 findPage 方法的实现

@Override
public PageVO<UserListVO> findPage(PageQuery<UserQuery> userQuery) {
    // 将查询对象 转换为 Mybatis Plus 的 Page 对象
    Page<AdminUser> page = Page.of(userQuery.getPageNum(), userQuery.getPageSize());
    UserQuery search = userQuery.getSearch();
    // 查询
    lambdaQuery()
        .eq(StrUtil.isNotBlank(search.getUsername()), AdminUser::getUsername, search.getUsername())
        .or()
        .like(StrUtil.isNotBlank(search.getNickname()), AdminUser::getNickname, search.getNickname())
        .page(page);
    // 将 Mybatis Plus 的 Page 对象 转换为 PageVO
    List<AdminUser> records = page.getRecords();
    List<UserListVO> userListVOs = BeanUtil.copyToList(records, UserListVO.class);
    return new PageVO<>(page.getTotal(), page.getPages(), userListVOs);
}

测试一下

到这里基本上已经完成了,但是细心的会发现我们没有处理排序字段,而且这种对象来回转换的方法非常繁琐。

三、进一步封装对象转换

对象转换处理:

基于上面的接口实现进一步完善,首先第一点,查询对象 转换为 Mybatis PlusPage 对象,我们先来完成这个封装。

你可以单独写到一个工具类里,这里我直接写在 PageQuery 对象中,这里方便我拿取参数,省的传参了,而且这样也更符合面向对象编程,这种转换能力应该属于 PageQuery 对象。

/**
 * 将当前对象转换为 MybatisPlus 分页对象
 *
 * @param <PO> PO类型
 * @return Page<PO>
 */
public <PO> Page<PO> toMpPage() {
    return Page.of(pageNum, pageSize);
}

那相同的 VO的转换能力应该由 PageVO提供,所以 VO转换写在 PageVO

/**
 * 将 MybatisPlus 分页结果转换为 PageDTO
 *
 * @param page        MybatisPlus 分页结果
 * @param targetClass 目标类型字节码
 * @param <V>         目标数据类型
 * @param <P>         原始数据类型
 * @return 分页结果 PageDTO
 */
public static <V, P> PageVO<V> of(Page<P> page, Class<V> targetClass) {
    List<P> records = page.getRecords();
    if (records.isEmpty()) {
        return empty(page);
    }
    // 将原始数据转换为目标数据 这里我使用了 hutool 的 BeanUtil,可以根据需要自行替换
    List<V> vs = BeanUtil.copyToList(records, targetClass);
    return new PageVO<>(page.getTotal(), page.getPages(), vs);
}


/**
 * 返回空的分页结果
 *
 * @param page MybatisPlus 分页结果
 * @param <V>  目标数据类型
 * @param <P>  原始数据类型
 * @return 分页结果 PageDTO
 */
public static <V, P> PageVO<V> empty(Page<P> page) {
    return new PageVO<>(page.getPages(), page.getPages(), Collections.emptyList());
}

这样我们之前的分页查询就可以写成这样

@Override
public PageVO<UserListVO> findPage(PageQuery<UserQuery> userQuery) {
    // 将查询对象 转换为 Mybatis Plus 的 Page 对象
    Page<AdminUser> page = userQuery.toMpPage();
    UserQuery search = userQuery.getSearch();
    // 查询
    lambdaQuery()
        .eq(StrUtil.isNotBlank(search.getUsername()), AdminUser::getUsername, search.getUsername())
        .or()
        .like(StrUtil.isNotBlank(search.getNickname()), AdminUser::getNickname, search.getNickname())
        .page(page);
    // 将 Mybatis Plus 的 Page 对象 转换为 PageVO
    return PageVO.of(page, UserListVO.class);
}

排序处理:

在我们处理将当前对象转换为 MybatisPlus分页对象的时候,只处理了 pageNumpageSize , 接下来我们处理一下排序的情况。

/**
 * 将当前对象转换为 MybatisPlus 分页对象
 *
 * @param <PO> PO类型
 * @return Page<PO>
 */
public <PO> Page<PO> toMpPage() {
    Page<PO> page = Page.of(pageNum, pageSize);
    if (orderItems != null && !orderItems.isEmpty()) {
        page.addOrder(orderItems);
    } else {
        // 如果不传默认根据创建时间倒序
        page.addOrder(OrderItem.desc("create_time"));
    }
    return page;
}

测试一下

==> Preparing: SELECT user_id, username, password, nickname, create_time, update_time, is_deleted FROM itshare_admin_user WHERE is_deleted = 0 ORDER BY user_id DESC LIMIT ?

控制台输出的 SQL 也如我们预期一样

多条件测试

==> Preparing: SELECT user_id, username, password, nickname, create_time, update_time, is_deleted FROM itshare_admin_user WHERE is_deleted = 0 ORDER BY user_id DESC, create_time ASC LIMIT ?

四、总结

这样我们基本上完成了项目中分页场景下的代码封装,后续分页场景,我们只需要定义好 xxxQuery 对象,以及 xxxVO 对象即可完成分页查询,大大简化了编码过程,提高了编码效率。其实就目前我们依然有很多具有共性的代码,比如对条件 sql 的编写,我们能不能根据对象类型以及前端配合传参动态去实现,这样我们就可以完全解放双手,定义两个对象就搞定一个分页接口的查询了。这个后面我会再写一篇文章和大家一起探讨一下。

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

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

相关文章

elementUI,设置日期,只能选择过去的和今天的日期

在 el-date-picker 组件中加&#xff1a;:picker-options"pickerOptions" <el-form-item label"票据生成日期&#xff1a;"> <el-date-picker v-model"date1" type"daterange" range-separator"至" value-format&…

Webpack 完整指南

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Webpack篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来webpack篇专栏内容:webpack介绍 目录 介绍 一、webpack 1.1、webpack是什么 1.2 webpack五个核心配置 1.…

学习threejs,拉伸几何体THREE.TubeGeometry管道

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️拉伸几何体THREE.TubeGeome…

医护人员排班|基于springBoot的医护人员排班系统设计与实现(附项目源码+论文+数据库)

私信或留言即免费送开题报告和任务书&#xff08;可指定任意题目&#xff09; 目录 一、摘要 二、相关技术 三、系统设计 四、数据库设计 五、核心代码 六、论文参考 七、源码获取 一、摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息…

js 对网页表格内容进行全选

起因&#xff0c; 目的: 需要从一个数据库&#xff0c;手动选取数据&#xff0c;而且这个网页没有全选按钮&#xff0c;只能一个一个点&#xff0c;很烦。 所以写了一个简单的 js 脚本&#xff0c; 一键全选。 过程: 代码 1 function clickAllBoxes() {const checkboxes do…

HarmonyNext保存Base64文件到Download下

本文介绍如何保存Base64的文件到Download下 参考文档地址&#xff1a; 保存用户文件-Harmony Next 用到的是DOWNLOAD模式保存文件 用户在使用save接口时&#xff0c;可以将pickerMode配置为DOWNLOAD模式&#xff0c;该模式下会拉起授权接口&#xff0c;用户确认后会在公共路径…

ChatGPT丨R语言在生态环境数据统计分析、绘图、模型中的应用

第一单元&#xff1a;生态环境数据统计概述 1.1 生态环境数据特点及统计方法介绍 1&#xff0e;生态环境数据复杂性和多样性 2&#xff0e;生态环境数据类型及分布特点 3&#xff0e;生态环境数据主要统计分析方法及统计检验&#xff08;t-检验、F检验、卡方检验&#xff0…

PostgreSQL Windows系统初始化、登录、创建用户及数据库

文章目录 PostgreSQL初始化PostgreSQL登录 PostgreSQL初始化 initdb 到安装目录下&#xff0c;找到目录E:\postgresql\bin&#xff08;自己的安装目录&#xff09;&#xff0c;在该目录下使用管理员方式打开cmd窗口。 initdb.exe -D "E:\postgresql\bin" E:\postgre…

车载软件架构---软件定义汽车的复杂性

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

闹钟、绘制与TCP

闹钟 pro文件&#xff1a; 头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTimer> #include<QTime> #include<QDebug> #include<QTimerEvent> #include<QDateTime> #include<QTextToSpeech> #i…

sql-labs靶场第十五关测试报告

目录 一、测试环境 1、系统环境 2、使用工具/软件 二、测试目的 三、操作过程 1、寻找注入点 2、注入数据库 ①寻找注入方法 ②爆库&#xff0c;查看数据库名称 ③爆表&#xff0c;查看security库的所有表 ④爆列&#xff0c;查看users表的所有列 ⑤成功获取用户名…

65.DDR3读写控制器的设计与验证(2)

&#xff08;1&#xff09;写FIFO和读FIFO的配置 &#xff08;2&#xff09;AXI接口写入控制代码&#xff1a; module axi_master_write (//全局变量input wire ACLK , //全局时钟input wire ARESETN , …

AI工具推荐合集

在数字化时代&#xff0c;AI工具的出现极大地改变了我们的工作和生活方式。无论是学术研究、创意设计还是日常写作&#xff0c;这些工具都能提供强大的支持。以下是一些值得推荐的AI工具&#xff0c;帮助您提高效率与创造力。 1. 包阅AI 官网链接: 包阅AI 包阅AI是一款智能A…

哪吒汽车的工资到底发没发?

联网圈第一预言家&#xff0c;也就是美团的王兴&#xff0c;早在新能源热钱最多的那几年就预言&#xff0c;未来中国汽车格局大概率是「3333」。 也就是3家央企、3家地方国企、3家民企和3家新势力…… 此言论一出&#xff0c;当时可以说惹急了很多车企&#xff0c;比如已经退…

Leetcode—1115. 交替打印 FooBar【中等】(多线程)

2024每日刷题&#xff08;180&#xff09; Leetcode—1115. 交替打印 FooBar C实现代码 class FooBar { private:int n;sem_t fooSem;sem_t barSem;public:FooBar(int n) {this->n n;sem_init(&fooSem, 0, 1);sem_init(&barSem, 0, 0);}~FooBar() {sem_destroy(&…

ARINC 429总线协议

一、概述 ARINC 是美国航空无线电公司英文字头的缩写&#xff0c; 该公司1977年7月21日出版了“ARINC 429规范”一书&#xff0c;429规范就是飞机电子系统之间数字式数据传输的标准格式&#xff0c;在飞机上使用429总线的电子设备均应遵守这个规范&#xff0c;这样才能保证电子…

Debug-029-el-table实现自动滚动分批请求数据

前情提要 最近做了一个小优化&#xff0c;还是关于展示大屏方面的。大屏中使用el-table展示列表数据&#xff0c;最初的方案是将数据全部返回&#xff0c;确实随着数据变多有性能问题&#xff0c;有时请求时间比较长。这里做的优化就是实现列表的滚动到距离底部一定高度时再次请…

如何处理多频段时序特征?这个Transformer变体显著提升预测效果

Transformer 模型已在时间序列预测中展现了卓越的性能。然而&#xff0c;在一些复杂场景中&#xff0c;它倾向于学习数据中的低频特征&#xff0c;而忽略了高频特征&#xff0c;表现出一种频率偏差。这种偏差阻碍了模型准确捕捉重要的高频数据特征。 本文介绍一篇来自 KDD 202…

菜鸟笔记006 截图识别文字插件 textOCR

随手可得的截图识别文字插件 textOCR&#xff0c;识别出来的文字可直接输入到illustrator的当前文档中&#xff1a; 执行条件 1、需截图软件支持&#xff0c;推荐笔记截图工具 2、截好图片直接拖入面板即可完成识别 ****后期可完成实现在illustrator选择图片对象完成文字识别。…

Docker-Harbor概述及构建

文章目录 一、Docker Harbor概述1.Harbor的特性2.Harbor的构成 二、搭建本地私有仓库三、部署 Docker-Harbor 服务四、在其他客户端上传镜像五、维护管理Harbor 一、Docker Harbor概述 Harbor 是 VMware 公司开源的企业级 Docker Registry 项目&#xff0c;其目标是帮助用户迅…