一.快速入门
1.引入MybatisPlus的起步依赖
mybatisPlus官方提供了starter。其中集成了Mybatis和MybatisPlus的所有功能,并且实现了自动装配效果。
因此我们可以用MybatisPlus的starter代替Mybatis的starter:
XML
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
2.定义Mapper
自定义的Mapper继承MybatisPlus提供的BaseMapper接口:
java
public interface UserMapper extends BaseMapper<User>{
}
3.常见注解
MybatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息。
-
类名驼峰转下划线作为表名
-
名为id的字段作为主键
-
变量名驼峰转下划线作为表的字段名
自定义配置:
如果不符合MybatisPlus的约定就要使用自定义配置。
-
@TableName:用来指定表名
idType枚举(不指定类型并且不给id赋值默认是ASSIGN_ID):
-
AUTO:数据库自增长
-
INPUT:通过set方法自行输入
-
ASSIGN_ID:分配id
-
-
@TableId:用来指定表中的主键字段信息
-
@TableField :用来指定表中的普通字段信息
使用@TableField的常见场景:
-
成员变量名与数据库字段名不一致
-
成员变量名以is开头,且是布尔值(经过反射处理,它会将is去掉作为数据库字段名)
-
成员变量名与数据库关键字冲突
-
成员变量名不是数据库字段(数据库中不存在该字段)
4.常用配置
XML
mybatis-plus:
type-aliases-package: com.gty.mp.domain.po #别名扫描包
mapper-locations: "classpath*:/mapper/**/*.xml" #mapper.xml文件地址, 默认值
configuration:
map-underscore-to-camel-case: true #是否开启下划线和驼峰的映射
cache-enabled: true #是否开启二级映射
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启mybatis打印日志
global-config:
db-config:
id-type: assign_id #id为雪花算法生成,注解配置大于全局配置
update-strategy: not_null # 更新策略:只更新非空字段
二.核心功能
5.条件构造器
-
QueryWrapper 和LambdaQueryWrapper通常用来构建select、delete、update的where条件部分
-
UpdateWrapper 和LambdaUpdateWrapper通常只有在set语句比较特殊才使用
-
尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码
6.自定义sql
我们可以利用MyBatisPlus的Wrapper来构建复杂的where条件,然后自己定义SQL语句中剩下的部分。
例如:将id为1,2,4的用户余额减200
7.IService接口
(1) IService接口使用流程:
- 自定义Service接口继承Service接口
- 自定义Service实现类,实现自定义接口并继承Servicelmpl类
(2) Lambda
IService中还提供了Lambda功能来简化我们的复杂查询及更新功能。
**例一:**根据一些复杂条件用lambda实现对用户的查询,查询条件如下:
a. name:用户名关键字,可以为空
b. status:用户状态,可以为空
c. minBalance:最小余额,可以为空
d. maxBalance:最大余额,可以为空
首先定义一个User的查询条件实体类,UserQuery实体,代码如下:
java
package com.gty.mp.model.query;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@ApiModel(description = "用户查询条件实体")
public class UserQuery{
@ApiModelProperty("用户名关键字")
private String name;
@ApiModelProperty("用户状态:1-正常,2-冻结")
private Integer status;
@ApiModelProperty("余额最小值")
private Integer minBalance;
@ApiModelProperty("余额最大值")
private Integer maxBalance;
}
接下来在UserController中定义一个controller方法:
java
@ApiOperation("根据复杂条件查询用户接口")
@GetMapping("/list")
public List<UserVO> queryUsers(UserQuery userQuery){
// 1.查询用户(PO)
List<User> users = userService.queryUsers(userQuery);
// 2.把po拷贝到vo
return BeanUtil.copyToList(users, UserVO.class);
}
在组织查询条件的时候,加入了 username != null 这样的参数,意思就是当条件成立时才会添加这个查询条件,类似Mybatis的mapper.xml文件中的<if>
标签。这样就实现了动态查询条件效果了。但是,上述操作过于复杂,因此IService中对LambdaQueryWrapper
和LambdaUpdateWrapper
的用法进一步做了简化。我们无需自己通过new
的方式来创建Wrapper
,而是直接调用lambdaQuery
和lambdaUpdate
方法:
Lambda查询:
java
@Override
public List<User> queryUsers(UserQuery userQuery) {
return lambdaQuery()
.like(userQuery.getName() != null, User::getUsername, userQuery.getName())
.eq(userQuery.getStatus() != null, User::getStatus, userQuery.getStatus())
.ge(userQuery.getMinBalance() != null, User::getBalance, userQuery.getMinBalance())
.le(userQuery.getMaxBalance() != null, User::getBalance, userQuery.getMaxBalance())
.list();
}
lambdaQuery方法中除了可以构建条件,还需要在链式编程的最后添加一个list()
,这是在告诉MP我们的调用结果需要是一个list集合。这里不仅可以用list()
,可选的方法有:
-
.one()
:最多1个结果 -
.list()
:返回集合结果 -
.count()
:返回计数结果
**例二:**根据id扣减用户余额,余额为0冻结用户
在扣减用户余额时,需要对用户剩余余额做出判断,如果发现剩余余额为0,则应该将status修改为2,这就是说update语句的set部分是动态的。
实现如下:
先在UserController中定义一个Controller方法:
java
@ApiOperation("根据id扣减用户余额,余额为0冻结用户接口")
@PutMapping("/{id}/deduction/{money}/lambda")
public void deductionBalance(
@ApiParam("用户id") @PathVariable("id") Long id,
@ApiParam("扣减的金额") @PathVariable("money") Integer money){
userService.deductionBalance(id,money);
}
接下来实现Lambda修改:
java
@Override
public void deductionBalance(Long id, Integer money) {
// 1.查询用户
User user = getById(id);
// 2.校验用户状态
if (user == null || user.getStatus() == UserStatus.FREEZE) {
throw new RuntimeException("用户状态异常!");
}
// 3.校验余额是否充足
if (user.getBalance() < money) {
throw new RuntimeException("用户余额不足");
}
// 4.扣减余额
int remainBalance = user.getBalance() - money;
lambdaUpdate()
.set(User::getBalance, remainBalance)
.set(remainBalance == 0, User::getStatus, UserStatus.FREEZE)
.eq(User::getId, id)
.eq(User::getBalance, user.getBalance()) //乐观锁
.update();
}
(3) 批量新增
首先测试逐条插入数据:
java
private User buildUser(int i) {
User user = new User();
user.setUsername("user_" + i);
user.setPassword("123");
user.setPhone("" + (18688190000L + i));
user.setBalance(2000);
user.setInfo(UserInfo.of(24, "英文老师", "female"));
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(user.getCreateTime());
return user;
}
/*
逐条插入
*/
@Test
void testSaveOneByOne() {
long b = System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
userService.save(buildUser(i));
}
long e = System.currentTimeMillis();
System.out.println("耗时:" + (e - b));
}
执行速度十分之慢!!!(因为太慢就不做演示了)
然后是mybatisPlus的批处理:
java
@Test
void testSaveBatch() {
// 准备10万条数据
List<User> list = new ArrayList<>(1000);
long b = System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
list.add(buildUser(i));
// 每1000条批量插入一次
if (i % 1000 == 0) {
userService.saveBatch(list);
list.clear();
}
}
long e = System.currentTimeMillis();
System.out.println("耗时:" + (e - b));
}
可以看到使用了批处理以后,比逐条新增效率提高了10倍左右,性能还是不错的。但是会发现,速度并不是很快。根据mybatisPlus源码显示,MybatisPlus
的批处理是基于PrepareStatement
的预编译模式,然后批量提交,最终在数据库执行时还是会有多条insert语句,逐条插入数据。而想得到最佳性能,最好是将多条SQL合并为一条。
做法如下:
MySQL的客户端连接参数中有这样的一个参数:rewriteBatchedStatements
。顾名思义,就是重写批处理的statement
语句。这个参数的默认值是false,我们需要修改连接参数,将其配置为true。
在配置文件中,在数据库连接配置中在url后面加上 rewriteBatchedStatements=true 的一个参数:
java
url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
在ClientPreparedStatement
的executeBatchInternal
中,有判断rewriteBatchedStatements
值是否为true并重写SQL的功能,这样效率就有了明显的提升。
三.扩展功能
1.代码生成器
(1)安装插件
(2) 使用
配置数据库信息
dbUrl的填写直接复制application中的数据库配置中的url即可:
2.静态工具
有的时候Service之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个静态工具类:Db
,其中的一些静态方法与IService
中方法签名基本一致,也可以帮助我们实现CRUD功能:
根据id查询用户和地址,测试如下:
首先在UserController中定义一个controller方法:
java
@ApiOperation("根据id查询用户和地址接口")
@GetMapping("/{id}/address")
public UserVO queryUserAndAddressById(@ApiParam("用户id") @PathVariable("id") Long id){
return userService.queryUserAndAddressById(id);
}
DB处理:
java
@Override
public UserVO queryUserAndAddressById(Long id) {
// 1.查询用户
User user = getById(id);
if (user == null || user.getStatus() == UserStatus.FREEZE) {
throw new RuntimeException("用户状态异常!");
}
// 2.查询地址
List<Address> addresses = Db.lambdaQuery(Address.class).eq(Address::getUserId, user.getId()).list();
// 3.封装VO
// 3.1转User的PO为VO
UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
// 3.2转地址VO
if (CollUtil.isNotEmpty(addresses)) {
userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
}
return userVO;
}
3.逻辑删除
逻辑删除就是基于代码逻辑模拟删除效果,但并不会真正删除数据。对于一些比较重要的数据,我们往往会采用逻辑删除的方案,即:
-
在表中添加一个字段标记数据是否被删除
-
当删除数据时把标记置为true
-
查询时过滤掉标记为true的数据
一旦采用了逻辑删除,所有的查询和删除逻辑都要跟着变化,非常麻烦。
为了解决这个问题,MybatisPlus就添加了对逻辑删除的支持,无需改变方法调用的方式,而是在底层帮我们自动CRUD的语句。我们要做的就是在application.yaml文件中配置逻辑删除的字段名称和值即可。
注意,只有MybatisPlus生成的SQL语句才支持自动的逻辑删除,自定义SQL需要自己手动处理逻辑删除。
XML
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
注意 ,逻辑删除本身也有自己的问题,比如:
-
会导致数据库表垃圾数据越来越多,影响查询效率
-
SQL中全都需要对逻辑删除字段做判断,影响查询效率。因此,我不太推荐采用逻辑删除功能,如果数据不能删除,可以采用把数据迁移到其它表的办法。
4.枚举处理器
①给枚举中的与数据库对应value值添加@EnumValue注解
③然后把User
类中的status
字段改为UserStatus
类型
④同时,为了使页面查询结果也是枚举格式,我们需要修改UserVO中的status属性
⑤在配置文件中配置统一的枚举处理器,实现类型转换
5.JSON处理器
①定义一个单独实体类来与info字段的属性匹配
代码如下:
java
@Data
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")
public class UserInfo {
private Integer age;
private String intro;
private String gender;
}
②接下来,将User类的info字段修改为UserInfo类型,并声明类型处理器:
java
@Data
@TableName(value = "user",autoResultMap = true)
public class User {
private Long id;
private String username;
private String password;
private String phone;
@TableField(typeHandler = JacksonTypeHandler.class)
private UserInfo info;
private UserStatus status;
private Integer balance;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
③同时,为了让页面返回的结果也以对象格式返回,我们要修改UserVO 中的info字段
四.插件功能
1.分页插件
(1)配置分页插件
代码如下:
java
package com.gty.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;
/**
* @author Gyu
* date 2024/10/18 17:56
* 功能描述:
*/
@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;
}
}
(2)分页API
编写分页查询测试:
java
@Test
void testPageQuery() {
// 1.准备分页条件
// 1.1分页条件
int pageNo = 1; //页码
int pageSize = 2; //每页大小
Page<User> page = Page.of(pageNo, pageSize);
// 1.2排序条件
page.addOrder(new OrderItem("balance",true)); //以用户余额进行升序排序
page.addOrder(new OrderItem("id",true)); //余额相等则用用户id进行升序排序
// 2.分页查询
userService.page(page);
// 3.解析
long total = page.getTotal(); //总条数
System.out.println("total = " + total);
long pages = page.getPages(); //总页数
System.out.println("pages = " + pages);
List<User> users = page.getRecords(); //第一页记录数
users.forEach(System.out::println);
}
运行结果如下:
这里用到了分页参数,Page,即可以支持分页参数,也可以支持排序参数。常见的API如下 :
2.通用分页实体
(1)实体
UserQuery之前已经定义过了,并且其中已经包含了过滤条件,其中缺少的仅仅是分页条件,而分页条件不仅仅用户分页查询需要,以后其它业务也都有分页查询的需求。因此建议将分页查询条件单独定义为一个PageQuery
实体:
PageQuery
是前端提交的查询参数,一般包含四个属性:
-
pageNo
:页码 -
pageSize
:每页数据条数 -
sortBy
:排序字段 -
isAsc
:是否升序
代码如下:
java
@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;
}
然后,让UserQuery继承这个实体:
java
package com.gty.mp.model.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;
}
返回值的用户实体沿用之前定一个UserVO
实体:
最后,则是分页实体PageDTO:
代码如下:
java
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("分页结果")
public class PageDTO<V> {
@ApiModelProperty("总条数")
private Long total;
@ApiModelProperty("总页数")
private Long pages;
@ApiModelProperty("集合")
private List<V> list;
}
(2)开发接口
在UserController
中定义分页查询用户的接口:
java
@ApiOperation("User的分页查询接口")
@GetMapping("/page")
public PageDTO<UserVO> queryUsersPage(UserQuery query){
return userService.queryUsersPage(query);
}
然后在IUserService
中创建queryUsersPage
方法:
java
PageDTO<UserVO> queryUsersPage(UserQuery query);
接下来,在UserServiceImpl
中实现该方法:
java
@Override
public PageDTO<UserVO> queryUsersPage(UserQuery query) {
// 1.构建条件
// 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(query.getName() != null, User::getUsername, query.getName())
.eq(query.getStatus() != null, User::getStatus, query.getStatus())
.page(page);
// 3.封装VO结果
PageDTO<UserVO> userPageDTO = new PageDTO<>();
// 3.1总条数
userPageDTO.setTotal(p.getTotal());
// 3.2总页数
userPageDTO.setPages(p.getPages());
// 3.3当前页数据
if(CollUtil.isEmpty(p.getRecords())){
userPageDTO.setList(Collections.emptyList());
return userPageDTO;
}
// 3.4拷贝user的vo
userPageDTO.setList(BeanUtil.copyToList(p.getRecords(), UserVO.class));
return userPageDTO;
}
(3) 封装通用分页功能
-
在上述代码中,从
PageQuery
到MybatisPlus
的Page
之间转换的过程还是比较麻烦的。 我们可以在PageQuery
这个实体中定义一个工具方法,简化开发。改造
PageQuery
类,可以定义一些工具方法,这样会简化在一些繁琐代码:
java
package com.gty.mp.model.query;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author Gyu
* date 2024/10/18 18:15
* 功能描述:
*/
@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;
/**
* 将查询到的对象转换为分页格式
*
* @param orders 传入OrderItem对象
* @param <T> 支持泛型
* @return 返回page对象
*/
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;
}
/**
* 将查询到的对象转换为分页格式,不需要传入new OrderItem对象,只需要排序字段和排序方式
*
* @param defaultSortBy 排序字段
* @param isAsc 排序方式
* @param <T> 支持泛型
* @return 返回page对象
*/
public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc) {
return this.toMpPage(new OrderItem(defaultSortBy, isAsc));
}
/**
* 将查询到的对象转换为分页格式,用create_time字段进行降序排序
*
* @param <T> 支持泛型
* @return 返回page对象
*/
public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {
return toMpPage("create_time", false);
}
/**
* 将查询到的对象转换为分页格式,用update_time字段进行降序排序
*
* @param <T> 支持泛型
* @return 返回page对象
*/
public <T> Page<T> toMpPageDefaultSortByUpdateTimeDesc() {
return toMpPage("update_time", false);
}
/**
* 将查询到的对象转换为分页格式,用id字段进行升序排序
*
* @param <T> 支持泛型
* @return 返回page对象
*/
public <T> Page<T> toMpPageDefaultSortByIdAsc() {
return toMpPage("id", true);
}
}
这样我们就可以省去在开发对从PageQuery
到Page
的的转换:
java
// 1.构建条件
/* // 1.1.分页条件
Page<User> page = Page.of(query.getPageNo(), query.getPageSize());
// 1.2排序条件(StrUtil.isNotBlank(): 检查字符串是否非空且非空白)
if(StrUtil.isNotBlank(query.getSortBy())){
page.addOrder(new OrderItem(query.getSortBy(),query.getIsAsc()));
}else{
// 为空默认按照更新时间排序
page.addOrder(new OrderItem("update_time",false));
}*/
Page<User> page = query.toMpPage(new OrderItem("update_time", true));
- 在查询出分页结果后,数据的非空校验,数据的vo转换都是模板代码,编写起来很是麻烦。我们也可以将其封装到
PageDTO
的工具方法中,简化整个过程。
改造PageDTO
类:
java
package com.gty.mp.model.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;
/**
* @author Gyu
* date 2024/10/18 18:18
* 功能描述:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("分页结果")
public class PageDTO<V> {
@ApiModelProperty("总条数")
private Long total;
@ApiModelProperty("总页数")
private Long pages;
@ApiModelProperty("集合")
private List<V> list;
/**
* 返回空分页结果
*
* @param p MybatisPlus的分页结果
* @param <V> 目标VO类型
* @param <P> 原始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 <V> 目标VO类型
* @param <P> 原始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 <V> 目标VO类型
* @param <P> 原始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
// 3.封装VO结果
/* PageDTO<UserVO> userPageDTO = new PageDTO<>();
// 3.1总条数
userPageDTO.setTotal(p.getTotal());
// 3.2总页数
userPageDTO.setPages(p.getPages());
// 3.3当前页数据
if(CollUtil.isEmpty(p.getRecords())){
userPageDTO.setList(Collections.emptyList());
return userPageDTO;
}
// 3.4拷贝user的vo
userPageDTO.setList(BeanUtil.copyToList(p.getRecords(), UserVO.class));*/
return PageDTO.of(p, UserVO.class);
最终代码简化为:
java
@Override
public PageDTO<UserVO> queryUsersPage(UserQuery query) {
// 1.构建条件
Page<User> page = query.toMpPage(new OrderItem("update_time", true));
// 2.分页查询
Page<User> p = lambdaQuery()
.like(query.getName() != null, User::getUsername, query.getName())
.eq(query.getStatus() != null, User::getStatus, query.getStatus())
.page(page);
// 3.封装VO结果
return PageDTO.of(p, UserVO.class);
}