当前位置: 首页 > news >正文

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

一、前言

作为一个 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 对象 转换为 PageVOList<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 对象 转换为 PageVOreturn 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.mrgr.cn/news/51855.html

相关文章:

  • 第九课:Python学习之函数基础
  • 2024年的5款AI写作工具,你用过几个?
  • 【含文档】基于Springboot+Vue的仓库管理系统设计与实现(含源码+数据库+lw)
  • 高级IO——五种IO模型
  • 5分钟精通Windows环境变量
  • Cesium的一些计算方法浅析(1)
  • 数据库->库的操作
  • The 48 bit pointer
  • 参加CSP-J/S 认证,要学多久C++才能达到获奖水平?
  • AI学习指南深度学习篇- 预训练模型的原理
  • 常用的网络配置命令
  • 揭秘提升3DMAX效率的6款必备神级插件!
  • 刷爆Leetcode Day2
  • PMP–知识卡片--项目生命周期与资源投入
  • EDA常见的拓扑结构
  • 2024软考网络工程师笔记 - 第5章.无线通信网
  • DP—子数组,子串系列 第一弹 -最大子数组和 -环形子数组的最大和 力扣
  • 程序指针简史
  • Golang | Leetcode Golang题解之第486题预测赢家
  • linux日志分割工具logorate快速验证配置是否有效