MyBatisPlus 封装分页方法示例
一、前言
作为一个 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 Plus 的 Page 对象,我们先来完成这个封装。
你可以单独写到一个工具类里,这里我直接写在 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分页对象的时候,只处理了 pageNum 和 pageSize , 接下来我们处理一下排序的情况。
/** * 将当前对象转换为 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 的编写,我们能不能根据对象类型以及前端配合传参动态去实现,这样我们就可以完全解放双手,定义两个对象就搞定一个分页接口的查询了。
到此这篇关于MyBatisPlus 封装分页方法示例的文章就介绍到这了,更多相关MyBatisPlus 分页内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
简化API提升开发效率RestTemplate与HttpClient OkHttp关系详解
这篇文章主要为大家介绍了简化API,提升开发效率,RestTemplate与HttpClient OkHttp关系介绍,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2023-10-10springboot application无法使用$获取pom变量的问题及解决
这篇文章主要介绍了springboot application无法使用$获取pom变量的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-02-02
最新评论