微服务day01
MybatisPlus
Mp入门
基本步骤
引入MybatisPlus依赖,代替Mybatis依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
定义Mapper接口并继承BaseMapper
public interface UserMapper extends BaseMapper<User> {
}
常见注解
MyBatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息。
- 类名驼峰转下划线作为表名
- 名为id的字段作为主键
- 变量名驼峰转下划线作为表的字段名
- @TableName:用来指定表名
- @TableId:用来指定表中的主键字段信息
- @TableField:用来指定表中的普通字段信息
IdType枚举:
- AUTO:数据库自增长
- INPUT:通过set方法自行输入
- ASSIGN_ID:分配 ID,接口IdentifierGenerator的方法nextId来生成id,默认实现类为DefaultIdentifierGenerator雪花算法
使用@TableField的常见场景:
- 成员变量名与数据库字段名不一致
- 成员变量名以is开头,且是布尔值
- 成员变量名与数据库关键字冲突 成员变量不是数据库字段
小结:
MybatisPlus是如何获取实现CRUD的数据库表信息的?
- 默认以类名驼峰转下划线作为表名
- 默认把名为id的字段作为主键
- 默认把变量名驼峰转下划线作为表的字段名
MybatisPlus的常用注解有哪些?
- @TableName:指定表名称及全局配置
- @TableId:指定id字段及相关配置
- @TableField:指定普通字段及相关配置
IdType的常见类型有哪些?
- AUTO、ASSIGN_ID、INPUT 使用
@TableField的常见场景是?
- 成员变量名与数据库字段名不一致
- 成员变量名以is开头,且是布尔值
- 成员变量名与数据库关键字冲突 成员变量不是数据库字段
常见配置
MyBatisPlus的配置项继承了MyBatis原生配置和一些自己特有的配置。例如:
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true #开启下划线和驼峰映射
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(配置后可以忽略不配置步骤二)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
update-strategy: not_null #更新策略(只更新非空字段)默认值
id-type: assign_id #id 使用雪花算法生成
type-aliases-package: com.itheima.mp.domain.po # 别名扫描包
mapper-locations: classpath*:mapper/*.xml
具体可参考官方文档:使用配置 | MyBatis-Plus (baomidou.com)
基本步骤小结:
MyBatisPlus使用的基本流程是什么?
- 引入起步依赖
- 自定义Mapper基础BaseMapper
- 在实体类上添加注解声明 表信息
- 在application.yml中根据需要添加配置
核心功能
条件构造器
条件构造器的小结
条件构造器的用法:
- QueryWrapper和LambdaQueryWrapper通常用来构建select、delete、update的where条件部分
- UpdateWrapper和LambdaUpdateWrapper通常只有在set语句比较特殊才使用
- 尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码
自定义SQL
实现:
void testBalesById(){
//定义要修改的数据
List<Long> ids = List.of(1L, 2L,4L);
int amount = 200;
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.in(User::getId, ids);
//自定义SQL方法调用
userMapper.updateBalanceByIds(wrapper, amount);
}
void updateBalanceByIds(@Param("ew") LambdaQueryWrapper<User> wrapper,@Param("amount") int amount);
<update id="updateBalanceByIds">
update user set balance = balance - #{amount} ${ew.customSqlSegment}
</update>
Server接口用法
实例:
接口
public interface UserService extends IService<User> {
}
实现类
@org.springframework.stereotype.Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
测试类
@SpringBootTest
class UserUserServiceImplTest {
@Autowired
private UserService service;
//新建数据
@Test
public void test(){
User user = new User();
user.setId(6L);
user.setUsername("laozhang");
user.setPassword("123");
user.setPhone("18688990011");
user.setBalance(200);
user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
service.save(user);
}
}
案例:
@Api(tags = "用户管理接口")
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor //必要的构造函数 ,Lombok注解
public class UserController {
// @Autowired //由于不推荐字段注入,所以使用构造函数进行注入
//final 表示该属性是必须的,则RequiredArgsConstructor会将其加入构造函数中
private final UserService userService;
@PostMapping
@ApiOperation("新增用户")
public void saveUser(@RequestBody UserFormDTO userdto){
User user = new User();
BeanUtil.copyProperties(userdto,user);
userService.save(user);
}
//删除yh
@DeleteMapping("/{id}")
@ApiOperation("删除用户")
public void deleteUser(@ApiParam("用户id") @PathVariable Long id){
userService.removeById(id);
}
@GetMapping("/{id}")
@ApiOperation("根据id查询用户")
public UserVO queryUserById(@ApiParam("用户id") @PathVariable Long id){
User user = userService.getById(id);
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user,userVO);
return userVO;
}
@GetMapping
@ApiOperation("根据id查询用户列表")
public List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids){
List<User> list = userService.listByIds(ids);
List<UserVO> userVOS = BeanUtil.copyToList(list, UserVO.class);
return userVOS;
}
@PutMapping("/{id}/deduction/{money}")
@ApiOperation("根据id扣减余额")
public void updataByid(@ApiParam("用户id")@PathVariable("id")int id,@ApiParam("扣除金额")@PathVariable("money") int money){
userService.updatamoneyByid(id,money);
}
}
public interface UserService extends IService<User> {
void updatamoneyByid(int id, int money);
}
@org.springframework.stereotype.Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public void updatamoneyByid(int id, int money) {
// 1.查询用户
User user = getById(id);
// 2.判断用户状态
if (user == null || user.getStatus() == 2) {
throw new RuntimeException("用户状态异常");
}
// 3.判断用户余额
if (user.getBalance() < money) {
throw new RuntimeException("用户余额不足");
}
// 4.扣减余额
baseMapper.deductMoneyById(id, money);
}
}
void deductMoneyById(int id, int money);
<update id="deductMoneyById">
update user set balance = balance - #{money} where id = #{id}
</update>
案例:IService的lambda查询
@Override
public List<UserVO> queryUserByCondition(UserQuery userQuery) {
LambdaQueryWrapper<User> wrapper = new QueryWrapper<User>().lambda()
.eq(userQuery.getName()!=null, User::getUsername, userQuery.getName())
.eq(userQuery.getStatus() != null, User::getStatus, userQuery.getStatus())
.lt(userQuery.getMinBalance() != null, User::getBalance, userQuery.getMinBalance())
.gt(userQuery.getMaxBalance() != null, User::getBalance, userQuery.getMaxBalance());
return BeanUtil.copyToList( userMapper.selectList(wrapper), UserVO.class);
}
@Override
public void updatamoneyByid(int id, int money) {
// 1.查询用户
User user = getById(id);
// 2.判断用户状态
if (user == null || user.getStatus() == 2) {
throw new RuntimeException("用户状态异常");
}
// 3.判断用户余额
if (user.getBalance() < money) {
throw new RuntimeException("用户余额不足");
}
// 4.扣减余额
// baseMapper.deductMoneyById(id, money);
int i = user.getBalance() - money;
LambdaUpdateWrapper<User> eq = new UpdateWrapper<User>()
.lambda()
.set(User::getBalance, i)
.set(i == 0, User::getStatus, 2)
.set(User::getBalance, user.getBalance()) //添加乐观锁,即在修改之前判断数据是否被其他线程修改
// 如果两数的值相等则可判断未被修改
.eq(User::getId, id);
this.update(eq);
}
IService批量新增
普通批处理
@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));
}
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("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(user.getCreateTime());
return user;
}
mp的批处理
@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));
}
修改项目中的application.yml文件,在jdbc的url后面添加参数&rewriteBatchedStatements=true
:
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true driver-class-name: com.mysql.cj.jdbc.Driver username: root password: MySQL123
扩展功能:
代码生成功能
静态工具
静态工具可以避免循环依赖
需求一:
@GetMapping("/{id}")
@ApiOperation("根据id查询用户")
public UserVO queryUserById(@ApiParam("用户id") @PathVariable Long id){
UserVO userVO = userService.queryUserandaddresById(id);
return userVO;
}
UserVO queryUserandaddresById(Long id);
@Override
public UserVO queryUserandaddresById(Long id) {
//根据id查询用户信息
User user = getById(id);
if (user == null|| user.getStatus() == 2){
throw new RuntimeException("用户状态异常");
}
//根据id查询地址信息
//使用静态工具来进行处理
List<Address> list = Db.lambdaQuery(Address.class).eq(Address::getUserId, id).list();
// 封装VO
UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
//判断地址是否为空
if (CollUtil.isNotEmpty(list)){
userVO.setAddressList(BeanUtil.copyToList(list, AddressVO.class));
}
return userVO;
}
需求二:
@GetMapping
@ApiOperation("根据id查询用户列表")
public List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids){
// List<User> list = userService.listByIds(ids);
// List<UserVO> userVOS = BeanUtil.copyToList(list, UserVO.class);
List<UserVO> userVOS = userService.queryUserandaddresByIds(ids);
return userVOS;
}
public List<UserVO> queryUserandaddresByIds(List<Long> ids) {
List<User> users = listByIds(ids);
if (CollUtil.isEmpty(users)){
throw new RuntimeException("用户不存在");
}
//获取用户id的集合
List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
List<Address> addressList = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
if (CollUtil.isEmpty(addressList)){
throw new RuntimeException("地址信息不存在");
}
//将集合按照用户id合并
List<AddressVO> addressVOlist = BeanUtil.copyToList(addressList, AddressVO.class);
List<UserVO> userVOlist = BeanUtil.copyToList(users, UserVO.class);
//使用Stream流的分组功能,根据id进行分组
Map<Long, List<AddressVO>> collect = addressVOlist.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
userVOlist.forEach(userVO -> userVO.setAddressList(collect.get(userVO.getId())));
return userVOlist;
}
需求三:
@RestController
@RequestMapping("/address")
@RequiredArgsConstructor
@Api(tags = "收货地址管理接口")
public class AddressController {
private final IAddressService addressService;
//根据用户id查询收货地址
@ApiOperation("根据用户id查询地址")
@GetMapping("/{userId}")
public List<AddressVO> queryAddressByUserId(@ApiParam("用户id") @PathVariable Long userId)
{
List<AddressVO> addressVOS = addressService.queryAddressByUserId(userId);
return addressVOS;
}
}
@Service
public class AddressServiceImpl extends ServiceImpl<AddressMapper, Address> implements IAddressService {
@Override
public List<AddressVO> queryAddressByUserId(Long userId) {
//根据用户id查询收货地址
LambdaQueryWrapper<Address> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Address::getUserId,userId);
List<Address> addressList = list(queryWrapper);
//判断用户状态
User user = Db.getById(userId, User.class);
if (user.getStatus() == 2){
throw new RuntimeException("用户状态异常");
}
return BeanUtil.copyToList(addressList, AddressVO.class);
}
}
逻辑删除
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true #开启下划线和驼峰映射
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名(配置后可以忽略不配置步骤二)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
update-strategy: not_null #更新策略(只更新非空字段)默认值
id-type: assign_id #id 使用雪花算法生成
type-aliases-package: com.itheima.mp.domain.po # 别名扫描包
mapper-locations: classpath*:mapper/*.xml
package com.itheima.mp.controller;
import com.itheima.mp.domain.po.Address;
import com.itheima.mp.service.IAddressService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class AddressControllerTest {
@Autowired
private IAddressService addressService;
@Test
public void deleteAddress(){
addressService.removeById(59);
}
@Test
public void queryAddress(){
Address addressServiceById = addressService.getById(59);
System.out.println("address:"+addressServiceById);
}
}
枚举处理器
使用步骤
- 在枚举类型中的价值属性添加 @EnumValue 注解
- 在配置文件中配置枚举处理器
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
@Getter
public enum UserStatus {
NORMAL(1,"正常"),
FREEZE(2,"冻结")
;
@EnumValue //用于指示mp要将此枚举值存储到数据库中
private Integer value;
@JsonValue //用于指示此枚举值在json中返回,否则返回的是枚举名称,而不是枚举值,
// 枚举值是数据库存储的值,而枚举名称是java代码中定义的
//JsonValue为序列化,EnumValue为反序列化
// JsonValue为Jackson注解,用于告诉springMvc要返回前端的数据
private String desc;
UserStatus(Integer value, String desc) {
this.value = value;
this.desc = desc;
}
}
修改po和vo
/**
* 使用状态(1正常 2冻结)
*/
private UserStatus status;
JSON处理器
@Data
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")//定义有参构造名称为of
public class UserInfo {
private Integer age;
private String intro;
private String gender;
}
package com.itheima.mp.domain.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.itheima.mp.enums.UserStatus;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName(autoResultMap = true,value = "user")
public class User {
/**
* 用户id
*/
private Long id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 注册手机号
*/
private String phone;
/**
* 详细信息
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private UserInfo info;
/**
* 使用状态(1正常 2冻结)
*/
private UserStatus status;
/**
* 账户余额
*/
private Integer balance;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}
package com.itheima.mp.domain.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.itheima.mp.domain.po.UserInfo;
import com.itheima.mp.enums.UserStatus;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(description = "用户VO实体")
@TableName(autoResultMap = true)
public class UserVO {
@ApiModelProperty("用户id")
private Long id;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("详细信息")
@TableField(typeHandler = JacksonTypeHandler.class)
private UserInfo info;
@ApiModelProperty("使用状态(1正常 2冻结)")
private UserStatus status;
@ApiModelProperty("账户余额")
private Integer balance;
@ApiModelProperty("地址列表")
private List<AddressVO> addressList;
}
插件功能
分页插件
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.Configuration;
@Configuration
public class MybatieConfig {
//mybatis-plus分页插件
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//创建核心拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加分页拦截器
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
//添加拦截器配置
paginationInnerInterceptor.setMaxLimit(100L);//查询最大值
//加入核心拦截器
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
}
@Test
public void test2(){
//分页查询测试
int pageNum = 1;
int pageSize = 10;
Page<User> page = new Page<>(pageNum, pageSize);
//添加分页查询的排序条件
//先按照第一个排序,在排第二个
page.addOrder(new OrderItem("balance",true));
page.addOrder(new OrderItem("id",false));
Page<User> page1 = service.page(page);
//展示数据
long total = page1.getTotal();
System.out.println("total:"+total);
System.out.println("current:"+page1.getCurrent());
System.out.println("size:"+page1.getSize());
page1.getRecords().forEach(System.out::println);
}
通用分页实体
案例:简单分页查询
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;
}
@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {
@ApiModelProperty("页码")
private Long pageNo;
@ApiModelProperty("页码")
private Long pageSize;
@ApiModelProperty("排序字段")
private String sortBy;
@ApiModelProperty("是否升序")
private Boolean isAsc;
}
package com.itheima.mp.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;
}
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;
}
开发接口:
package com.itheima.mp.controller;
import com.itheima.mp.domain.dto.PageDTO;
import com.itheima.mp.domain.query.PageQuery;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.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);
}
// 。。。 略
}
@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);
}
改造PageQuery实体
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);
}
}
// 1.构建条件
Page<User> page = query.toMpPageDefaultSortByCreateTimeDesc();
.改造PageDTO实体
package com.itheima.mp.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 <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);
}
}
化简:
@Override
public PageDTO<UserVO> queryUserByPage(PageQuery query) {
// 1.构建条件
Page<User> page = query.toMpPageDefaultSortByCreateTimeDesc();
// 2.查询
page(page);
// 3.封装返回
return PageDTO.of(page, UserVO.class);
}
@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;
});
}