• MyBatis-Plus 实战教程四 idea插件


    插件功能

    MybatisPlus提供了很多的插件功能,进一步拓展其功能。目前已有的插件有:

    • PaginationInnerInterceptor:自动分页
    • TenantLineInnerInterceptor:多租户
    • DynamicTableNameInnerInterceptor:动态表名
    • OptimisticLockerInnerInterceptor:乐观锁
    • IllegalSQLInnerInterceptor:sql 性能规范
    • BlockAttackInnerInterceptor:防止全表更新与删除

    分页插件

    在未引入分页插件的情况下,MybatisPlus是不支持分页功能的,IService和BaseMapper中的分页方法都无法正常起效。
    所以,我们必须配置分页插件。

    配置分页插件

    在项目中新建一个配置类:

    其代码如下:

    package com.onenewcode.mpdemo.config;
    
    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MybatisConfig {
    
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            // 初始化核心插件
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            // 添加分页插件
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            return interceptor;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    分页API

    编写一个分页查询的测试:

    @Test
    void testPageQuery() {
        // 1.分页查询,new Page()的两个参数分别是:页码、每页大小
        Page<User> p = userService.page(new Page<>(2, 2));
        // 2.总条数
        System.out.println("total = " + p.getTotal());
        // 3.总页数
        System.out.println("pages = " + p.getPages());
        // 4.数据
        List<User> records = p.getRecords();
        records.forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    运行的SQL如下:
    请添加图片描述

    这里用到了分页参数,Page,即可以支持分页参数,也可以支持排序参数。常见的API如下:

    
    int pageNo = 1, pageSize = 5;
    // 分页参数
    Page<User> page = Page.of(pageNo, pageSize);
    // 排序参数, 通过OrderItem来指定
    page.addOrder(new OrderItem("balance", false));
    
    userService.page(page);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    通用分页实体

    现在要实现一个用户分页查询的接口,接口规范如下:

    参数说明
    请求方式GET
    请求路径/users/page
    请求参数{ “pageNo”: 1, “pageSize”: 5, “sortBy”: “balance”, “isAsc”: false, “name”: “o”, “status”: 1 }
    返回值{ “total”: 100006, “pages”: 50003, “list”: [ { “id”: 1685100878975279298, “username”: “user_9****”, “info”: { “age”: 24, “intro”: “英文老师”, “gender”: “female” }, “status”: “正常”, “balance”: 2000 } ] }
    特殊说明- 如果排序字段为空,默认按照更新时间排序 - 排序字段不为空,则按照排序字段排序

    这里需要定义3个实体:

    • UserQuery:分页查询条件的实体,包含分页、排序参数、过滤条件
    • PageDTO:分页结果实体,包含总条数、总页数、当前页数据
    • UserVO:用户页面视图实体

    实体

    由于UserQuery之前已经定义过了,并且其中已经包含了过滤条件,具体代码如下:

    package com.onenewcode.mpdemo.domain.query;
    
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    
    @Data
    @ApiModel(description = "用户查询条件实体")
    public class UserQuery {
        @ApiModelProperty("用户名关键字")
        private String name;
        @ApiModelProperty("用户状态:1-正常,2-冻结")
        private Integer status;
        @ApiModelProperty("余额最小值")
        private Integer minBalance;
        @ApiModelProperty("余额最大值")
        private Integer maxBalance;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    其中缺少的仅仅是分页条件,而分页条件不仅仅用户分页查询需要,以后其它业务也都有分页查询的需求。因此建议将分页查询条件单独定义为一个PageQuery实体.
    PageQuery是前端提交的查询参数,一般包含四个属性:

    • pageNo:页码
    • pageSize:每页数据条数
    • sortBy:排序字段
    • isAsc:是否升序
    @Data
    @ApiModel(description = "分页查询实体")
    public class PageQuery {
        @ApiModelProperty("页码")
        private Integer pageNo;
        @ApiModelProperty("页码")
        private Integer pageSize;
        @ApiModelProperty("排序字段")
        private String sortBy;
        @ApiModelProperty("是否升序")
        private Boolean isAsc;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    然后,让我们的UserQuery继承这个实体:

    package com.onenewcode.mpdemo.domain.query;
    
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    
    @EqualsAndHashCode(callSuper = true)
    @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;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    返回值的用户实体沿用之前定一个UserVO实体:

    最后,则是分页实体PageDTO:

    代码如下:

    
    package com.onenewcode.mpdemo.domain.dto;
    
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    
    import java.util.List;
    
    @Data
    @ApiModel(description = "分页结果")
    public class PageDTO<T> {
        @ApiModelProperty("总条数")
        private Long total;
        @ApiModelProperty("总页数")
        private Long pages;
        @ApiModelProperty("集合")
        private List<T> list;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    开发接口

    我们在UserController中定义分页查询用户的接口:

    package com.onenewcode.mpdemo.controller;
    
    import com.onenewcode.mpdemo.domain.dto.PageDTO;
    import com.onenewcode.mpdemo.domain.query.PageQuery;
    import com.onenewcode.mpdemo.domain.vo.UserVO;
    import com.onenewcode.mpdemo.service.UserService;
    import lombok.RequiredArgsConstructor;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("users")
    @RequiredArgsConstructor
    public class UserController {
    
        private final UserService userService;
    
        @GetMapping("/page")
        public PageDTO<UserVO> queryUsersPage(UserQuery query){
            return userService.queryUsersPage(query);
        }
    
        // 。。。 略
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    然后在IUserService中创建queryUsersPage方法:

    PageDTO<UserVO> queryUsersPage(PageQuery query);
    
    • 1

    接下来,在UserServiceImpl中实现该方法:

    
    @Override
    public PageDTO<UserVO> queryUsersPage(PageQuery 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.查询
        page(page);
        // 3.数据非空校验
        List<User> records = page.getRecords();
        if (records == null || records.size() <= 0) {
            // 无数据,返回空结果
            return new PageDTO<>(page.getTotal(), page.getPages(), Collections.emptyList());
        }
        // 4.有数据,转换
        List<UserVO> list = BeanUtil.copyToList(records, UserVO.class);
        // 5.封装返回
        return new PageDTO<UserVO>(page.getTotal(), page.getPages(), list);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    改造PageQuery实体

    在刚才的代码中,从PageQuery到MybatisPlus的Page之间转换的过程还是比较麻烦的。

    我们完全可以在PageQuery这个实体中定义一个工具方法,简化开发。
    像这样:

    package com.onenewcode.mpdemo.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;
        }
    
        public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc){
            return this.toMpPage(new OrderItem(defaultSortBy, isAsc));
        }
    
        public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {
            return toMpPage("create_time", false);
        }
    
        public <T> Page<T> toMpPageDefaultSortByUpdateTimeDesc() {
            return toMpPage("update_time", false);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    这样我们在开发也时就可以省去对从PageQuery到Page的的转换:
    // 1.构建条件

    Page<User> page = query.toMpPageDefaultSortByCreateTimeDesc();
    
    
    • 1
    • 2

    改造PageDTO实体

    在查询出分页结果后,数据的非空校验,数据的vo转换都是模板代码,编写起来很麻烦。
    我们完全可以将其封装到PageDTO的工具方法中,简化整个过程:

    package com.onenewcode.mpdemo.domain.dto;
    
    import cn.hutool.core.bean.BeanUtil;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    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 PageDTO<V> {
        private Long total;
        private Long pages;
        private List<V> list;
    
        /**
         * 返回空分页结果
         * @param p MybatisPlus的分页结果
         * @param  目标VO类型
         * @param 

    原始PO类型 * @return VO的分页对象 */ public static <V, P> PageDTO<V> empty(Page<P> p){ return new PageDTO<>(p.getTotal(), p.getPages(), Collections.emptyList()); } /** * 将MybatisPlus分页结果转为 VO分页结果 * @param p MybatisPlus的分页结果 * @param voClass 目标VO类型的字节码 * @param 目标VO类型 * @param

    原始PO类型 * @return VO的分页对象 */ public static <V, P> PageDTO<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 PageDTO<>(p.getTotal(), p.getPages(), vos); } /** * 将MybatisPlus分页结果转为 VO分页结果,允许用户自定义PO到VO的转换方式 * @param p MybatisPlus的分页结果 * @param convertor PO到VO的转换函数 * @param 目标VO类型 * @param

    原始PO类型 * @return VO的分页对象 */ public static <V, P> PageDTO<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 PageDTO<>(p.getTotal(), p.getPages(), vos); } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    最终,业务层的代码可以简化为:

    @Override
    public PageDTO<UserVO> queryUserByPage(PageQuery query) {
        // 1.构建条件
        Page<User> page = query.toMpPageDefaultSortByCreateTimeDesc();
        // 2.查询
        page(page);
        // 3.封装返回
        return PageDTO.of(page, UserVO.class);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    @Override
    public PageDTO<UserVO> queryUserByPage(PageQuery query) {
        // 1.构建条件
        Page<User> page = query.toMpPageDefaultSortByCreateTimeDesc();
        // 2.查询
        page(page);
        // 3.封装返回
        return PageDTO.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;
        });
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    仓库地址

    https://github.com/onenewcode/mp-demo.git
    
    • 1
  • 相关阅读:
    AGV 控制Python
    [Error]Swift开发调试时使用LLDB的po和print命令无法输出变量
    试题:最大的矩形(给定直方图里面积最大的矩形)
    Element - el-table 表头列字段筛选 支持一个页面多table
    有没有了解流量压力特性曲线的,求解释
    洛谷 P2763 试题库问题(最大流 EK算法)
    c# 设计一个图书管理系统
    css关于relative和absolute的区别
    关于#jenkins集成apifox#的问题,坐等彭于晏回答
    模型 金字塔原理
  • 原文地址:https://blog.csdn.net/studycodeday/article/details/134095881