【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】

4.插件功能

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

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

注意:
使用多个分页插件的时候需要注意插件定义顺序,建议使用顺序如下:

  • 多租户,动态表名
  • 分页,乐观锁
  • sql 性能规范,防止全表更新与删除

这里我们以分页插件为里来学习插件的用法。

4.1.分页插件

在未引入分页插件的情况下,MybatisPlus是不支持分页功能的,IServiceBaseMapper中的分页方法都无法正常起效。

所以,我们必须配置分页插件。

4.1.1.配置分页插件

在项目中新建一个配置类:
【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第1张图片
其代码如下:

package com.itheima.mp.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();
        // 添加分页插件
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        paginationInnerInterceptor.setMaxLimit(1000L);
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }
}
4.1.2.分页API

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

  @Test
    void testPageQuery(){
        int pageNo = 1, pageSize = 2;
    //     1.准备分页条件
    //     1.1.分页条件
        Page<User> page = Page.of(pageNo, pageSize);
    //     1.2.排序条件 (先按照balance 升序,如果存在一致的balance 再按照 id 进行升序排序)
        page.addOrder(new OrderItem("balance",true));
        page.addOrder(new OrderItem("id",true));

    //     2.分页查询
        Page<User> p = userService.page(page);
    //     3.解析
        long total = p.getTotal(); // 总记录数
        System.out.println("total = "+total);
        long pages = p.getPages(); // 总页数
        System.out.println("pages = "+pages);
        List<User> users = p.getRecords(); // 分页结果
        users.forEach(System.out::println);
    }

运行的SQL如下:
【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第2张图片

4.2.通用分页实体

现在要实现一个用户分页查询的接口,接口规范如下:
【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第3张图片
返回值如下:
【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第4张图片

这里需要定义3个实体:

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

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

package com.itheima.mp.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;
}

其中缺少的仅仅是分页条件,而分页条件不仅仅用户分页查询需要,以后其它业务也都有分页查询的需求。因此建议将分页查询条件单独定义为一个PageQuery实体:

【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第5张图片

PageQuery是前端提交的查询参数,一般包含四个属性:

  • pageNo:页码
  • pageSize:每页数据条数
  • sortBy:排序字段
  • isAsc:是否升序
package com.itheima.mp.domain.query;

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

@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {
    @ApiModelProperty("页码")
    private Long pageNo;
    @ApiModelProperty("页码")
    private Long pageSize;
    @ApiModelProperty("排序字段")
    private String sortBy;
    @ApiModelProperty("是否升序")
    private Boolean isAsc;
}

然后,让我们的UserQuery继承这个实体:
【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第6张图片

分页实体PageDTO:
【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第7张图片

代码如下:

package com.itheima.mp.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;
}

最后是UserVO,在之前有写过:
【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第8张图片

4.2.2.开发接口

UserController中定义分页查询用户的接口:
【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第9张图片
代码如下:

  @ApiOperation("根据分页条件查询用户的接口")
    @GetMapping("/page")
    public PageDTO<UserVO> queryUsersPage(UserQuery query){
        return userService.queryUsersPage(query);
    }

然后在IUserService中创建queryUsersPage方法:

PageDTO<UserVO> queryUsersPage(PageQuery query);

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

  @Override
    public PageDTO<UserVO> queryUsersPage(UserQuery query) {
        String name = query.getName();
        Integer status = query.getStatus();
        Integer maxBalance = query.getMaxBalance();
        Integer minBalance = query.getMinBalance();
        // 1.1.分页条件
        Page<User> page = Page.of(query.getPageNo(), query.getPageSize());
        // 1.2.排序条件
        if (StrUtil.isNotBlank(query.getSortBy())) { // 排序条件不为空
            page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));
        } else { // 排序条件为空,默认按照更新时间进行降序排序
            page.addOrder(new OrderItem("update_time", false));
        }
        // 2.分页查询
        Page<User> p = lambdaQuery()
                .like(name != null, User::getUsername, name)
                .eq(status != null, User::getStatus, status)
                .ge(minBalance != null, User::getBalance, minBalance)
                .le(maxBalance != null, User::getBalance, maxBalance)
                .page(page);

        // 3.封装VO结果
        PageDTO<UserVO> dto = new PageDTO<>();
        // 3.1.总条数
        dto.setTotal(p.getTotal());
        // 3.2.总页数
        dto.setPages(p.getPages());
        // 3.3.当前页数据
        List<User> records = p.getRecords();
        if (CollUtil.isEmpty(records)) {
            dto.setList(Collections.emptyList());
            return dto;
        }
        // 3.4.拷贝user的vo
        dto.setList(BeanUtil.copyToList(records, UserVO.class));
        // 4.返回
        return dto;
    }

启动项目,在测试效果:
【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第10张图片

4.2.3.改造PageQuery实体

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

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

【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第11张图片

package com.itheima.mp.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);
    }
}

这样可以省去对从PageQueryMybatisPlusPage的转换,UserServiceImpl简化如下:
【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第12张图片

Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();
4.2.4.改造PageDTO实体

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

【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第13张图片
代码如下:

package com.itheima.mp.domain.dto;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModel;
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
@ApiModel(description = "分页结果")
public class PageDTO<T> {
    @ApiModelProperty("总条数")
    private Long total;
    @ApiModelProperty("总页数")
    private Long pages;
    @ApiModelProperty("集合")
    private List<T> 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); } }

最终业务简化如下:

【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第14张图片
代码如下:

  @Override
    public PageDTO<UserVO> queryUsersPage(UserQuery query) {
        String name = query.getName();
        Integer status = query.getStatus();
        Integer maxBalance = query.getMaxBalance();
        Integer minBalance = query.getMinBalance();
        // 1.构建分页条件
        Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();
        // 2.分页查询
        Page<User> p = lambdaQuery()
                .like(name != null, User::getUsername, name)
                .eq(status != null, User::getStatus, status)
                .ge(minBalance != null, User::getBalance, minBalance)
                .le(maxBalance != null, User::getBalance, maxBalance)
                .page(page);
        // 3.封装返回
        return PageDTO.of(page, UserVO.class);
    }

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

  @Override
    public PageDTO<UserVO> queryUsersPage(UserQuery query) {
        String name = query.getName();
        Integer status = query.getStatus();
        Integer maxBalance = query.getMaxBalance();
        Integer minBalance = query.getMinBalance();
        // 1.构建分页条件
        Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();
        // 2.分页查询
        Page<User> p = lambdaQuery()
                .like(name != null, User::getUsername, name)
                .eq(status != null, User::getStatus, status)
                .ge(minBalance != null, User::getBalance, minBalance)
                .le(maxBalance != null, User::getBalance, maxBalance)
                .page(page);
        // 3.封装返回
        // return PageDTO.of(page, UserVO.class);
        
        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;
     });
    }

查询的结果如下:

【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】_第15张图片

你可能感兴趣的:(Java,java,学习,笔记)