MyBatisPlus 封装分页方法示例

 更新时间:2024年12月03日 10:36:20   作者:曹申阳  
本文主要介绍了基于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 分页内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • activemq整合springboot使用方法(个人微信小程序用)

    activemq整合springboot使用方法(个人微信小程序用)

    这篇文章主要介绍了activemq整合springboot使用(个人微信小程序用),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • MyBatis源码剖析之Mapper代理方式详解

    MyBatis源码剖析之Mapper代理方式详解

    这篇文章主要为大家详细介绍了MyBatis中Mapper代理的方式,文中将通过源码为大家进行详细的剖析,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-07-07
  • 浅谈几种Java自定义异常处理方式

    浅谈几种Java自定义异常处理方式

    在Java中,异常是一种常见的处理机制,本文主要介绍了浅谈几种Java自定义异常处理方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • Maven工程路径映射的实现示例

    Maven工程路径映射的实现示例

    本文主要介绍了Maven工程路径映射的实现示例,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-04-04
  • Java SpringBoot 获取接口实现类汇总

    Java SpringBoot 获取接口实现类汇总

    这篇文章主要介绍了Java SpringBoot 获取接口实现类汇总,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-09-09
  • Java上传文件错误java.lang.NoSuchMethodException的解决办法

    Java上传文件错误java.lang.NoSuchMethodException的解决办法

    今天小编就为大家分享一篇关于Java上传文件错误java.lang.NoSuchMethodException的解决办法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • Java 并发编程ArrayBlockingQueue的实现

    Java 并发编程ArrayBlockingQueue的实现

    这篇文章主要介绍了Java 并发编程ArrayBlockingQueue的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • 使用nexus3.X上传本地jar包并且通过pom读取的解决方案(全网最新)

    使用nexus3.X上传本地jar包并且通过pom读取的解决方案(全网最新)

    这篇文章主要介绍了使用nexus3.X上传本地jar包并且通过pom读取的解决方案(全网最新),本文内容有点长,结合图文实例给大家讲解的非常详细,需要的朋友可以参考下
    2023-11-11
  • spring中的BeanFactory与FactoryBean的讲解

    spring中的BeanFactory与FactoryBean的讲解

    今天小编就为大家分享一篇关于spring中的BeanFactory与FactoryBean的讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • java实现短地址服务的方法(附代码)

    java实现短地址服务的方法(附代码)

    大多数情况下URL太长,字符多,不便于发布复制和存储,本文就介绍了通过java实现短地址服务,减少了许多使用太长URL带来的不便,需要的朋友可以参考下
    2015-07-07

最新评论