微服务day01

MybatisPlus

Mp入门

基本步骤

引入MybatisPlus依赖,代替Mybatis依赖

XML 复制代码
<dependency>  
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>        
    <version>3.5.3.1</version>
</dependency>

定义Mapper接口并继承BaseMapper

java 复制代码
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原生配置和一些自己特有的配置。例如:

XML 复制代码
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使用的基本流程是什么?

  1. 引入起步依赖
  2. 自定义Mapper基础BaseMapper
  3. 在实体类上添加注解声明 表信息
  4. 在application.yml中根据需要添加配置

核心功能

条件构造器
条件构造器的小结

条件构造器的用法:

  • QueryWrapper和LambdaQueryWrapper通常用来构建select、delete、update的where条件部分
  • UpdateWrapper和LambdaUpdateWrapper通常只有在set语句比较特殊才使用
  • 尽量使用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码
自定义SQL

实现:

java 复制代码
    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);
    }
java 复制代码
 void updateBalanceByIds(@Param("ew") LambdaQueryWrapper<User> wrapper,@Param("amount") int amount);
XML 复制代码
    <update id="updateBalanceByIds">
        update user set balance = balance - #{amount} ${ew.customSqlSegment}
    </update>
Server接口用法
实例:

接口

java 复制代码
public interface UserService extends IService<User> {
}

实现类

java 复制代码
@org.springframework.stereotype.Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

}

测试类

java 复制代码
@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);
    }

}
案例:
java 复制代码
@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);
    }

}
java 复制代码
public interface UserService extends IService<User> {
    void updatamoneyByid(int id, int money);
}
java 复制代码
@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);
    }
}
java 复制代码
    void deductMoneyById(int id, int money);
XML 复制代码
    <update id="deductMoneyById">
        update user set balance = balance - #{money} where id = #{id}
    </update>
案例:IService的lambda查询
java 复制代码
    @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);
    }
java 复制代码
    @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批量新增

普通批处理

java 复制代码
@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的批处理

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));
}

修改项目中的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

扩展功能:

代码生成功能
静态工具

静态工具可以避免循环依赖

需求一:

java 复制代码
    @GetMapping("/{id}")
    @ApiOperation("根据id查询用户")
    public UserVO queryUserById(@ApiParam("用户id") @PathVariable Long id){
        UserVO userVO = userService.queryUserandaddresById(id);
        return userVO;
    }
java 复制代码
    UserVO queryUserandaddresById(Long id);
java 复制代码
    @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;
    }

需求二:

java 复制代码
    @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;
    }
java 复制代码
    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;
    }

需求三:

java 复制代码
@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;
    }
}
java 复制代码
@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);
    }
}
逻辑删除
XML 复制代码
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
java 复制代码
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);
    }

}
枚举处理器

使用步骤

  1. 在枚举类型中的价值属性添加 @EnumValue 注解
  2. 在配置文件中配置枚举处理器
XML 复制代码
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
java 复制代码
@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

java 复制代码
    /**
     * 使用状态(1正常 2冻结)
     */
    private UserStatus status;
JSON处理器
java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")//定义有参构造名称为of
public class UserInfo {
    private Integer age;
    private String intro;
    private String gender;
}
java 复制代码
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;
}
java 复制代码
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;
}
插件功能
分页插件
java 复制代码
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;
    }
}
java 复制代码
    @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);
    }
通用分页实体
案例:简单分页查询
java 复制代码
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;
}
java 复制代码
@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {
    @ApiModelProperty("页码")
    private Long pageNo;
    @ApiModelProperty("页码")
    private Long pageSize;
    @ApiModelProperty("排序字段")
    private String sortBy;
    @ApiModelProperty("是否升序")
    private Boolean isAsc;
}
java 复制代码
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;
}
java 复制代码
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;
}

开发接口:

java 复制代码
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);
    }

    // 。。。 略
}
java 复制代码
@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实体

java 复制代码
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);
    }
}
java 复制代码
// 1.构建条件
Page<User> page = query.toMpPageDefaultSortByCreateTimeDesc();

.改造PageDTO实体

java 复制代码
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);
    }
}

化简:

java 复制代码
@Override
public PageDTO<UserVO> queryUserByPage(PageQuery query) {
    // 1.构建条件
    Page<User> page = query.toMpPageDefaultSortByCreateTimeDesc();
    // 2.查询
    page(page);
    // 3.封装返回
    return PageDTO.of(page, UserVO.class);
}
java 复制代码
@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;
    });
}
相关推荐
寻星探路4 小时前
【深度长文】万字攻克网络原理:从 HTTP 报文解构到 HTTPS 终极加密逻辑
java·开发语言·网络·python·http·ai·https
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
曹牧7 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
KYGALYX7 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
爬山算法7 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate