4 插件功能
-
MybatisPlus提供了很多的插件功能,进一步拓展其功能。目前已有的插件有:
PaginationInnerInterceptor
:自动分页TenantLineInnerInterceptor
:多租户DynamicTableNameInnerInterceptor
:动态表名OptimisticLockerInnerInterceptor
:乐观锁IllegalSQLInnerInterceptor
:sql 性能规范BlockAttackInnerInterceptor
:防止全表更新与删除
-
注意: 使用多个分页插件的时候需要注意插件定义顺序,建议使用顺序如下:
- 多租户,动态表名
- 分页,乐观锁
- sql 性能规范,防止全表更新与删除
4.1 分页插件
4.1.1 配置分页插件
-
在项目中新建一个配置类:
-
其代码如下:
package com.shisan.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); // 设置最大单页限制数量为1000条,默认 500 条,-1 不受限制 // 添加分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
4.1.2 分页API
-
编写一个分页查询的测试:
@Test void testPageQuery() { int pageNo = 1, pageSize = 2; //分页条件 Page<User> page = Page.of(pageNo, pageSize); //排序条件 page.addOrder(new OrderItem("balance", true)); page.addOrder(new OrderItem("id", true)); //分页查询 Page<User> p = userService.page(page); //解析 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); }
-
结果:
4.2 通用分页实体
-
需求:遵循下面的接口规范,编写一个UserController接口,实现User的分页查询;
参数 说明 请求方式 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
:用户页面视图实体
4.2.1 实体
-
UserQuery之前已经定义过了,并且其中已经包含了过滤条件,具体代码如下:
package com.shisan.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
实体:PageQuery
是前端提交的查询参数,一般包含四个属性:pageNo
:页码pageSize
:每页数据条数sortBy
:排序字段isAsc
:是否升序
package com.shisan.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继承这个实体:
-
返回值的用户实体沿用之前定义的
UserVO
实体:
- 分页实体PageDTO:
package com.shisan.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;
}
4.2.2 开发接口
-
在
UserController
中定义分页查询用户的接口:@ApiOperation("根据复杂条件分页查询用户接口") @GetMapping("/page") public PageDTO<UserVO> queryUsersPage(UserQuery query){ return userService.queryUsersPage(query); }
-
在
IUserService
中创建queryUsersPage
方法:PageDTO<UserVO> queryUsersPage(UserQuery query);
-
在
UserServiceImpl
中实现该方法@Override public PageDTO<UserVO> queryUsersPage(UserQuery query) { String name = query.getName(); Integer status = query.getStatus(); //1、构建条件 //分页条件 Page<User> page = Page.of(query.getPageNo(), query.getPageSize()); //排序条件 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, name) .page(page); //3、封装VO PageDTO<UserVO> dto = new PageDTO<>(); //总条数 dto.setTotal(p.getTotal()); //总页数 dto.setPages(p.getPages()); //当前页数据 List<User> records = p.getRecords(); if(CollUtil.isEmpty(records)){ dto.setList(Collections.emptyList()); return dto; } List<UserVO> vos = BeanUtil.copyToList(records, UserVO.class); dto.setList(vos); //4、返回 return dto; }
4.2.3 改造PageQuery实体和PageDTO实体
-
在上节的代码中,从
PageQuery
到MybatisPlus
的Page
之间转换的过程还是比较麻烦的。可以在PageQuery
这个实体中定义一个工具方法,将PageQuery
对象转为MP中的Page对象,简化开发;package com.shisan.mp.domain.query; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.shisan.mp.domain.po.User; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @Data @ApiModel(description = "分页查询实体") public class PageQuery { @ApiModelProperty("页码") private Long pageNo = 1L; //给默认值 @ApiModelProperty("页码") private Long pageSize = 5L; @ApiModelProperty("排序字段") private String sortBy; @ApiModelProperty("是否升序") private Boolean isAsc = true; public <T> Page<T> toMpPage(OrderItem... orderItems) { //OrderItem用来存放业务层调用该方法时传递过来的默认排序条件 //1、构建条件 //分页条件 Page<T> page = Page.of(pageNo, pageSize); //排序条件 if (StrUtil.isNotBlank(sortBy)) { //若排序字段不为空 page.addOrder(new OrderItem(sortBy, isAsc)); } else if (orderItems != null) { //若排序字段为空,按照默认条件排序 page.addOrder(orderItems); } return page; } public <T> Page<T> toMpPage(String defaultSortBy, Boolean defaultAsc) { //按照默认条件排序 return toMpPage(new OrderItem(defaultSortBy, defaultAsc)); } public <T> Page<T> toMpPageDefaultSortByCreateTime() { //默认按照创建时间倒序排序 return toMpPage(new OrderItem("create_time", false)); } public <T> Page<T> toMpPageDefaultSortByUpdateTime() { //默认按照修改时间倒序排序 return toMpPage(new OrderItem("update_time", false)); } }
-
在查询出分页结果后,数据的非空校验,数据的vo转换都是模板代码,编写起来很麻烦。可以将其封装到PageDTO的工具方法中,在该方法中,将MP中的Page结果转为PageDTO结果。简化整个过程:
package com.shisan.mp.domain.dto; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.Collections; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; @Data @ApiModel(description = "分页结果") public class PageDTO<T> { @ApiModelProperty("总条数") private Long total; @ApiModelProperty("总页数") private Long pages; @ApiModelProperty("集合") private List<T> list; public static <PO, VO> PageDTO<VO> of(Page<PO> p, Function<PO, VO> convertor){ PageDTO<VO> dto = new PageDTO<>(); //总条数 dto.setTotal(p.getTotal()); //总页数 dto.setPages(p.getPages()); //当前页数据 List<PO> records = p.getRecords(); if(CollUtil.isEmpty(records)){ dto.setList(Collections.emptyList()); return dto; } //拷贝user的VO dto.setList(records.stream().map(convertor).collect(Collectors.toList())); //返回 return dto; } }
-
修改UserServiceImpl.java中的代码:
@Override public PageDTO<UserVO> queryUsersPage(UserQuery query) { String name = query.getName(); Integer status = query.getStatus(); //1、构建条件 Page<User> page = query.toMpPageDefaultSortByUpdateTime(); //2、分页查询 Page<User> p = lambdaQuery() .like(name != null, User::getUsername, name) .eq(status != null, User::getStatus, name) .page(page); return PageDTO.of(p, user -> { //1、拷贝基础属性 UserVO vo = BeanUtil.copyProperties(user, UserVO.class); //2、处理特殊逻辑 vo.setUsername(vo.getUsername().substring(0, vo.getUsername().length() - 2) + "**"); return vo; }); }