十二、MybatisPlus-插件功能-分页插件(2)-通用分页封装

213 阅读5分钟

本文是系列文章,目录:
一、MybatisPlus-基本使用
二、MybatisPlus-进阶使用-条件构造器
三、MybatisPlus-进阶使用-自定义sql
四、MybatisPlus-进阶使用-Service接口(1)-基本使用
五、MybatisPlus-进阶使用-Service接口(2)-自定义service
六、MybatisPlus-进阶使用-Service接口(3)- 批量新增
七、MybatisPlus-进阶使用-逻辑删除
八、MybatisPlus-进阶使用-枚举处理器
九、MybatisPlus-进阶使用-JSON类型处理器
十、MybatisPlus-进阶使用-配置文件加密
十一、MybatisPlus-插件功能-分页插件(1)
十二、MybatisPlus-插件功能-分页插件(2)-通用分页封装
十三、MybatisPlus-插件功能-乐观锁插件
十四、MybatisPlus-插件功能-sql性能分析
十五、MybatisPlus-自动填充字段
MybatisPlus-问题汇总

简介

上一章中,我们对mybatis-plus的分页插件进行了简单使用,在本章中,我们对分页功能进行封装,使之使用起来更方便。

实体分析

在一般的查询接口开发过程中,我们可能需要以下几个实体:

  1. PageQuery: 包含当前页数每页条数排序字段是否升序

  2. EitityQuery: 包含查询条件分页信息(分页、排序参数等)。

    tips:分页信息一般通过继承分页实体(PageQuery)来获得。

  3. EitityVO: 查询结果视图。

  4. PageDTO: 包含总条数总页数查询结果集合

分页封装

以用户查询为例

1.定义相关实体

PageQuery.java

package com.pino.demo.domain.query;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Data;

@Data
public class PageQuery {
    private Integer pageNo;
    private Integer pageSize;
    private String sortBy;
    private Boolean isAsc;

    public <T>  Page<T> toMpPage(OrderItem ... orders){
        // 1.分页条件
        Page<T> p = Page.of(pageNo, pageSize);
        // 2.排序条件
        // 2.1.先看前端有没有传排序字段
        if (sortBy != null) {
            p.addOrder(new OrderItem(sortBy, isAsc));
            return p;
        }
        // 2.2.再看有没有手动指定排序字段
        if(orders != null){
            p.addOrder(orders);
        }
        return p;
    }
}

UserQuery.java

package com.pino.demo.domain.query;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
@ApiModel(description = "用户查询条件实体")
public class UserQuery extends PageQuery{
    @ApiModelProperty("用户名关键字")
    private String name;
    @ApiModelProperty("用户状态:1-正常,2-冻结")
    private Integer status;
    @ApiModelProperty("余额最小值")
    private Integer minBalance;
    @ApiModelProperty("余额最大值")
    private Integer maxBalance;
}

UserVO.java

package com.pino.demo.domain.vo;

import com.pino.demo.domain.po.UserInfo;
import com.pino.demo.enums.UserStatus;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;


@Data
@ApiModel(description = "用户VO实体")
public class UserVO{

    @ApiModelProperty("用户id")
    private Long id;

    @ApiModelProperty("用户名")
    private String username;

    @ApiModelProperty("详细信息")
    private UserInfo info;

    @ApiModelProperty("使用状态(1正常 2冻结)")
    private UserStatus status;

    @ApiModelProperty("账户余额")
    private Integer balance;
}

PageResult.java

package com.pino.demo.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageResult<T> {
    @ApiModelProperty("总条数")
    private Long total;
    @ApiModelProperty("总页数")
    private Long pages;
    @ApiModelProperty("查询结果")
    private List<T> list;
}

2.接口开发

2.1 在UserController.java中定义分页查询接口。

@GetMapping("/page")
public PageResult<UserVO> queryUsersPage(UserQuery query){
    return userService.queryUsersPage(query);
}

2.2 在IUserService.java中定义分页查询方法。

PageResult<UserVO> queryUsersPage(UserQuery query);

2.3 在UserServiceImpl中实现该方法

@Override
public PageResult<UserVO> queryUsersPage(UserQuery query) {
    // 1.构建条件
    // 1.1.分页条件
    Page<User> page = Page.of(query.getPageNo(), query.getPageSize());
    // 1.2.排序条件
    if (query.getSortBy() != null) {
       page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));
    }else{
       // 默认按照更新时间排序
       page.addOrder(new OrderItem("update_time", false));
    }
    // 2.查询
    // 1.组织条件
    String username = query.getName();
    Integer status = query.getStatus();
    Integer minBalance = query.getMinBalance();
    Integer maxBalance = query.getMaxBalance();
    lambdaQuery().like(username != null, User::getUsername, username)
          .eq(status != null, User::getStatus, status)
          .ge(minBalance != null, User::getBalance, minBalance)
          .le(maxBalance != null, User::getBalance, maxBalance)
          .page(page);
    // 3.数据非空校验
    List<User> records = page.getRecords();
    if (records == null || records.isEmpty()) {
       // 无数据,返回空结果
       return new PageResult<>(page.getTotal(), page.getPages(), Collections.emptyList());
    }
    // 4.有数据,转换
    List<UserVO> list = BeanUtil.copyToList(records, UserVO.class);
    // 5.封装返回
    return new PageResult<>(page.getTotal(), page.getPages(), list);
}

2.4 测试

所有数据:image.png

请求参数:

http://localhost:8080/users/page?pageNo=2&pageSize=2

查询结果:

{
  "total": 4,
  "pages": 2,
  "list": [
    {
      "id": 2,
      "username": "Rose",
      "info": {
        "age": 19,
        "intro": "大角色",
        "gender": "female"
      },
      "status": "正常",
      "balance": 600
    },
    {
      "id": 1,
      "username": "Tom",
      "info": {
        "age": 20,
        "intro": "打野",
        "gender": "male"
      },
      "status": "冻结",
      "balance": 1200
    }
  ]
}

3.优化PageQuery和PageResult

有两个可以优化的点:

  1. PageQueryMybatisPlusPage之间转换的过程 (优化构建条件)
  2. 在查询出分页结果后,数据的非空校验,数据的vo转换过程 (优化查询结果)

我们可以分别在PageQueryPageResult 提供工具方法来优化这两个点。

3.1 优化构建条件 PageQuery.java

package com.pino.demo.domain.query;

import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
public class PageQuery {
    @ApiModelProperty("当前页")
    private Integer pageNo;
    @ApiModelProperty("每页条数")
    private Integer pageSize;
    @ApiModelProperty("排序字段")
    private String sortBy;
    @ApiModelProperty("是否升序")
    private Boolean isAsc;

    public <T>  Page<T> toMpPage(OrderItem ... orders){
        // 1.分页条件
        Page<T> p = Page.of(pageNo, pageSize);
        // 2.排序条件
        // 2.1.先看前端有没有传排序字段
        if (sortBy != null) {
            p.addOrder(new OrderItem(sortBy, isAsc));
            return p;
        }
        // 2.2.再看有没有手动指定排序字段
        if(orders != null){
            p.addOrder(orders);
        }
        return p;
    }
    public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {
       return toMpPage(new OrderItem("create_time", false));
    }

    public <T> Page<T> toMpPageDefaultSortByUpdateTimeDesc() {
       return toMpPage(new OrderItem("update_time", false));
    }
}

3.2 优化查询结果 PageResult.java

package com.pino.demo.domain.dto;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageResult<T> {
    @ApiModelProperty("总条数")
    private Long total;
    @ApiModelProperty("总页数")
    private Long pages;
    @ApiModelProperty("查询结果")
    private List<T> list;

    /**
     * 返回空分页结果
     * @param p MybatisPlus的分页结果
     * @param <T> 目标VO类型
     * @param <P> 原始PO类型
     * @return VO的分页对象
     */
    public static <T, P> PageResult<T> empty(Page<P> p){
        return new PageResult<>(p.getTotal(), p.getPages(), Collections.emptyList());
    }

    /**
     * 将MybatisPlus分页结果转为 VO分页结果
     * @param p MybatisPlus的分页结果
     * @param voClass 目标VO类型的字节码
     * @param <V> 目标VO类型
     * @param <P> 原始PO类型
     * @return VO的分页对象
     */
    public static <V, P> PageResult<V> of(Page<P> p, Class<V> voClass) {
        // 1.非空校验
        List<P> records = p.getRecords();
        if (records == null || records.size() <= 0) {
            // 无数据,返回空结果
            return empty(p);
        }
        // 2.数据转换
        List<V> vos = BeanUtil.copyToList(records, voClass);
        // 3.封装返回
        return new PageResult<>(p.getTotal(), p.getPages(), vos);
    }

    /**
     * 将MybatisPlus分页结果转为 VO分页结果,允许用户自定义PO到VO的转换方式
     * @param p MybatisPlus的分页结果
     * @param convertor PO到VO的转换函数
     * @param <V> 目标VO类型
     * @param <P> 原始PO类型
     * @return VO的分页对象
     */
    public static <V, P> PageResult<V> of(Page<P> p, Function<P, V> convertor) {
        // 1.非空校验
        List<P> records = p.getRecords();
        if (records == null || records.size() <= 0) {
            // 无数据,返回空结果
            return empty(p);
        }
        // 2.数据转换
        List<V> vos = records.stream().map(convertor).collect(Collectors.toList());
        // 3.封装返回
        return new PageResult<>(p.getTotal(), p.getPages(), vos);
    }
}

3.3 最终业务层代码简化

@Override
public PageResult<UserVO> querySimpleUsersPage(UserQuery query) {
    // 1.构建分页查询条件
    Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();
    // 2.构建业务查询条件并进行分页查询
    String username = query.getName();
    Integer status = query.getStatus();
    Integer minBalance = query.getMinBalance();
    Integer maxBalance = query.getMaxBalance();
    lambdaQuery().like(username != null, User::getUsername, username)
          .eq(status != null, User::getStatus, status)
          .ge(minBalance != null, User::getBalance, minBalance)
          .le(maxBalance != null, User::getBalance, maxBalance)
          .page(page);
    // 3. 进行数据非空校验和vo转换
    return PageResult.of(page, UserVO.class);
}

如果是希望自定义PO到VO的转换过程,可以这样做:

@Override
public PageResult<UserVO> querySimpleUsersPage(UserQuery query) {
    // 1.构建分页查询条件
    Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();
    // 2.构建业务查询条件并进行分页查询
    String username = query.getName();
    Integer status = query.getStatus();
    Integer minBalance = query.getMinBalance();
    Integer maxBalance = query.getMaxBalance();
    lambdaQuery().like(username != null, User::getUsername, username)
          .eq(status != null, User::getStatus, status)
          .ge(minBalance != null, User::getBalance, minBalance)
          .le(maxBalance != null, User::getBalance, maxBalance)
          .page(page);
    // 3. 进行数据非空校验和vo转换
    return PageResult.of(page, user -> {
       // 拷贝属性到VO
       UserVO vo = BeanUtil.copyProperties(user, UserVO.class);
       // 用户名脱敏
       String userName = vo.getUsername();
       vo.setUsername(userName.substring(0, userName.length() - 2) + "**");
       return vo;
    });
}

查询结果:

{
  "total": 4,
  "pages": 2,
  "list": [
    {
      "id": 2,
      "username": "Ro**",
      "info": {
        "age": 19,
        "intro": "大角色",
        "gender": "female"
      },
      "status": "正常",
      "balance": 600
    },
    {
      "id": 1,
      "username": "T**",
      "info": {
        "age": 20,
        "intro": "打野",
        "gender": "male"
      },
      "status": "冻结",
      "balance": 1200
    }
  ]
}