• 微服务开发与实战Day01 - MyBatisPlus


    一、微服务

    概念:微服务是一种软件架构风格,它是以专注于单一职责的很多小型项目为基础,组合除复杂的大型应用。

    课程安排:

    https://www.bilibili.com/video/BV1S142197x7/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=26bfae91aa8be97ba3634570e2041c28

    二、MybatisPlus

    MyBatis-Plus 🚀 为简化开发而生

    1. 快速入门

    1.1 入门案例

    需求:基于课前资料提供的项目,实现下列功能:

    • ①新增用户功能
    • ②根据id查询用户
    • ③根据id批量查询用户
    • ④根据id更新用户
    • ⑤根据id删除用户

    (1)导入项目到IDEA

    (2)执行SQL脚本

    ①启动SQL服务

    ②打开SQL命令行窗口,复制粘贴资料里的SQL脚本,执行

    ③在IDEA中打开数据库表

    这里记得把项目中application.yaml中的数据库连接密码也改成自己的

    (3)安装插件 - MyBatisx 

    点击左侧的小鸟可以快速切换到XML文档

    使用步骤:

    1. 引入MyBatisPlus的起步依赖 - pom.xml

    MyBatisPlus官方提供了starter,其中集成了MyBatis和MyBatisPlus的所有功能,并且实现了自动装配效果。因此,我们可以使用MyBatisPlus的starter代替MyBatis的starter:

    1. <dependency>
    2. <groupId>com.baomidougroupId>
    3. <artifactId>mybatis-plus-boot-starterartifactId>
    4. <version>3.5.3.1version>
    5. dependency>

    2. 定义Mapper - userMapper.java

    自定义的Mapper继承MyBatisPlus提供的BaseMapper,注意要指定泛形

    1. package com.itheima.mp.mapper;
    2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    3. import com.itheima.mp.domain.po.User;
    4. import org.apache.ibatis.annotations.Mapper;
    5. @Mapper // 不要忘记加上@Mapper注解
    6. public interface UserMapper extends BaseMapper {
    7. // ... ...
    8. }

    如果在测试时遇到下面的问题,把pom.xml中的lombok依赖版本改为1.18.30(因为JDK22对应的lombok版本为1.18.30)

    java: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field 'com.sun.tools.javac.tree.JCTree qualid'

    1. <dependency>
    2. <groupId>org.projectlombokgroupId>
    3. <artifactId>lombokartifactId>
    4. <version>1.18.30version>
    5. dependency>

    3. 删除src/main/resources/mapper/UserMapper.xml里的内容如下

    4. 把UserMapperTest.java改成如下,亲测全部测试通过

    1. package com.itheima.mp.mapper;
    2. import com.itheima.mp.domain.po.User;
    3. import org.junit.jupiter.api.Test;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.boot.test.context.SpringBootTest;
    6. import java.time.LocalDateTime;
    7. import java.util.List;
    8. @SpringBootTest
    9. class UserMapperTest {
    10. @Autowired
    11. private UserMapper userMapper;
    12. @Test
    13. void testInsert() {
    14. User user = new User();
    15. user.setId(5L);
    16. user.setUsername("Lucy");
    17. user.setPassword("123");
    18. user.setPhone("18688990011");
    19. user.setBalance(200);
    20. user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
    21. user.setCreateTime(LocalDateTime.now());
    22. user.setUpdateTime(LocalDateTime.now());
    23. userMapper.insert(user);
    24. }
    25. @Test
    26. void testSelectById() {
    27. User user = userMapper.selectById(5L);
    28. System.out.println("user = " + user);
    29. }
    30. @Test
    31. void testQueryByIds() {
    32. List users = userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L));
    33. users.forEach(System.out::println);
    34. }
    35. @Test
    36. void testUpdateById() {
    37. User user = new User();
    38. user.setId(5L);
    39. user.setBalance(20000);
    40. userMapper.updateById(user);
    41. }
    42. @Test
    43. void testDeleteUser() {
    44. userMapper.deleteById(5L);
    45. }
    46. }

    1.2 常见注解

    注解配置 | MyBatis-Plus

    MyBatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库信息。

    MyBatisPlus中比较常用的几个注解如下:

    • @TableName:用来指定表名
    • @TableId:用来指定表中的主键字段信息
    • @TableField:用来指定表中的普通字段信息

    1.3 常见配置

    使用配置 | MyBatis-Plus

    MyBatisPlus的配置项继承了MyBatis原生配置和一些自己特有的配置。例如:

    ①修改application.yaml

    1. spring:
    2. datasource:
    3. url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    4. driver-class-name: com.mysql.cj.jdbc.Driver
    5. username: root
    6. password: 123456
    7. logging:
    8. level:
    9. com.itheima: debug
    10. pattern:
    11. dateformat: HH:mm:ss
    12. mybatis-plus:
    13. type-aliases-package: com.itheima.mp.domain.po
    14. global-config:
    15. db-config:
    16. id-type: auto

    2. 核心功能

    2.1 条件构造器

    MyBatisPlus支持各种复杂的where条件,可以满足日常开发的所有需求。

    案例1:基于QueryWrapper的查询

    需求:

    ①查询出名字中带o的,存款大于等于1000元的人的id、username、info、balance字段

    1. SELECT id, username, info, balance
    2. FROM user
    3. WHERE username LIKE ? > AND balance >= ?
    1. // 基于QueryWrapper
    2. @Test
    3. void testQueryWrapper() {
    4. // 1. 构建查询条件
    5. QueryWrapper wrapper = new QueryWrapper()
    6. .select("id", "username", "info", "balance")
    7. .like("username", "o")
    8. .ge("balance", 1000);
    9. List users = userMapper.selectList(wrapper);
    10. users.forEach(System.out::println);
    11. }
    1. // 基于LambdaQueryWrapper
    2. @Test
    3. void testLambdaQueryWrapper() {
    4. // 1. 构建查询条件
    5. LambdaQueryWrapper wrapper = new LambdaQueryWrapper()
    6. .select(User::getId, User::getUsername, User::getInfo, User::getBalance)
    7. .like(User::getUsername, "o")
    8. .ge(User::getBalance, 1000);
    9. List users = userMapper.selectList(wrapper);
    10. users.forEach(System.out::println);
    11. }

    ②更新用户名为jack的用户的余额为2000

    1. UPDATE user
    2. SET balance = 2000
    3. WHERE username = "jack"
    1. @Test
    2. void testUpdateByQueryWrapper() {
    3. // 1. 要更新的数据
    4. User user = new User();
    5. user.setBalance(2000);
    6. // 2. 更新的条件
    7. QueryWrapper wrapper = new QueryWrapper().eq("username", "jack");
    8. // 3. 执行更新
    9. userMapper.update(user, wrapper);
    10. }

    案例2:基于UpdateWrapper的更新

    需求:更新id为1,2,4的用户余额,扣200

    1. UPDATE user
    2. SET balance = balance - 200
    3. WHERE id in (1, 2, 4)
    1. @Test
    2. void testUpdateWrapper() {
    3. List ids = List.of(1L, 2L, 4L);
    4. UpdateWrapper wrapper = new UpdateWrapper()
    5. .setSql("balance = balance - 200")
    6. .in("id", ids);
    7. userMapper.update(null, wrapper);
    8. }

    总结

    条件构造器的用法:

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

    2.2 自定义SQL

    我们可以利用MyBatisPlus的Wrapper来构建复杂的Where条件,然后自己定义SQL语句中剩下的部分。

    案例1:自定义SQL

    需求:将id在指定范围内的用户(如1、2、4)的余额扣减指定值

    ①基于Wrapper构建where条件

    ②在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew

    ③自定义SQL,并使用Wrapper条件

    2.3 Service接口

    使用示例:

    ①自定义service接口继承 IService接口

    IUserService.java

    1. package com.itheima.mp.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.itheima.mp.domain.po.User;
    4. public interface IUserService extends IService {
    5. }

    ②自定义Service实现类,实现自定义接口并继承 ServiceImpl类

    UserServiceImpl.java

    1. package com.itheima.mp.service.impl;
    2. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    3. import com.itheima.mp.domain.po.User;
    4. import com.itheima.mp.mapper.UserMapper;
    5. import com.itheima.mp.service.IUserService;
    6. import org.springframework.stereotype.Service;
    7. @Service
    8. public class UserServiceImpl extends ServiceImpl implements IUserService {
    9. }

    IUserServiceTest.java

    1. package com.itheima.mp.service;
    2. import com.itheima.mp.domain.po.User;
    3. import org.junit.jupiter.api.Test;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.boot.test.context.SpringBootTest;
    6. import java.time.LocalDateTime;
    7. import java.util.List;
    8. import static org.junit.jupiter.api.Assertions.*;
    9. @SpringBootTest
    10. class IUserServiceTest {
    11. @Autowired
    12. private IUserService userService;
    13. // 新增
    14. @Test
    15. void testSaveUser() {
    16. User user = new User();
    17. user.setUsername("Lily");
    18. user.setPassword("123");
    19. user.setPhone("18358995031");
    20. user.setBalance(20000);
    21. user.setInfo("{\"age\": 24, \"intro\": \"物理老师\", \"gender\": \"female\"}");
    22. user.setCreateTime(LocalDateTime.now());
    23. user.setUpdateTime(LocalDateTime.now());
    24. userService.save(user);
    25. }
    26. // 查询
    27. @Test
    28. void testQuery() {
    29. List users = userService.listByIds((List.of(1L, 2L, 4L)));
    30. users.forEach(System.out::println);
    31. }
    32. }

    案例1:基于Restful风格实现下列接口

    需求:基于Restful风格实现下面的接口:

    文档地址:Docs

    ①在项目中引入依赖 - pom.xml

    1. <dependency>
    2. <groupId>com.github.xiaoymingroupId>
    3. <artifactId>knife4j-openapi2-spring-boot-starterartifactId>
    4. <version>4.1.0version>
    5. dependency>
    6. <dependency>
    7. <groupId>org.springframework.bootgroupId>
    8. <artifactId>spring-boot-starter-webartifactId>
    9. dependency>

    ②配置swagger信息 - application.yaml

    1. knife4j:
    2. enable: true
    3. openapi:
    4. title: 用户管理接口文档
    5. description: "用户管理接口文档"
    6. email: zhanghuyi@itcast.cn
    7. concat: 虎哥
    8. url: https://www.itcast.cn
    9. version: v1.0.0
    10. group:
    11. default:
    12. group-name: default
    13. api-rule: package
    14. api-rule-resources:
    15. - com.itheima.mp.controller

    ③UserController.java

    1. package com.itheima.mp.controller;
    2. import cn.hutool.core.bean.BeanUtil;
    3. import com.itheima.mp.domain.dto.UserFormDTO;
    4. import com.itheima.mp.domain.po.User;
    5. import com.itheima.mp.domain.vo.UserVO;
    6. import com.itheima.mp.service.IUserService;
    7. import io.swagger.annotations.Api;
    8. import io.swagger.annotations.ApiOperation;
    9. import io.swagger.annotations.ApiParam;
    10. import lombok.RequiredArgsConstructor;
    11. import org.springframework.web.bind.annotation.*;
    12. import java.util.List;
    13. @Api(tags = "用户管理接口")
    14. @RequestMapping("/users")
    15. @RestController
    16. @RequiredArgsConstructor
    17. public class UserController {
    18. private final IUserService userService;
    19. @ApiOperation("新增用户接口")
    20. @PostMapping
    21. public void saveUser(@RequestBody UserFormDTO userDTO) {
    22. // 1. 把DTO拷贝到PO(hutool)
    23. User user = BeanUtil.copyProperties(userDTO, User.class);
    24. // 新增
    25. userService.save(user);
    26. }
    27. @ApiOperation("删除用户接口")
    28. @DeleteMapping("{id}")
    29. public void deleteUserById(@ApiParam("用户id") @PathVariable("id") Long id) {
    30. userService.removeById(id);
    31. }
    32. @ApiOperation("根据id查询用户接口")
    33. @GetMapping("{id}")
    34. public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long id) {
    35. // 1. 查询用户PO
    36. User user = userService.getById(id);
    37. // 2. 把PO拷贝到VO
    38. return BeanUtil.copyProperties(user, UserVO.class);
    39. }
    40. @ApiOperation("根据id批量查询用户接口")
    41. @GetMapping
    42. public List queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List ids) {
    43. // 1. 查询用户PO
    44. List users = userService.listByIds(ids);
    45. // 2. 把PO拷贝到VO
    46. return BeanUtil.copyToList(users, UserVO.class);
    47. }
    48. @ApiOperation("扣减用户余额接口")
    49. @PutMapping("/{id}/deduction/{money}")
    50. public void deductBalance(
    51. @ApiParam("用户id") @PathVariable("id") Long id,
    52. @ApiParam("扣减的金额") @PathVariable("money") Integer money) {
    53. userService.deductBalance(id, money);
    54. }
    55. }

    ④IUserService.java

    1. package com.itheima.mp.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.itheima.mp.domain.po.User;
    4. public interface IUserService extends IService {
    5. void deductBalance(Long id, Integer money);
    6. }

    ⑤UserServiceImpl.java

    1. package com.itheima.mp.service.impl;
    2. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    3. import com.itheima.mp.domain.po.User;
    4. import com.itheima.mp.mapper.UserMapper;
    5. import com.itheima.mp.service.IUserService;
    6. import org.springframework.stereotype.Service;
    7. @Service
    8. public class UserServiceImpl extends ServiceImpl implements IUserService {
    9. @Override
    10. public void deductBalance(Long id, Integer money) {
    11. // 1. 查询用户
    12. User user = getById(id);
    13. // 2. 校验用户状态
    14. if(user == null || user.getStatus() == 2) {
    15. throw new RuntimeException("用户状态异常!");
    16. }
    17. // 3. 校验用户余额是否充足
    18. if(user.getBalance() < money) {
    19. throw new RuntimeException("用户余额不足!");
    20. }
    21. // 4. 扣减余额
    22. baseMapper.deductBalance(id, money);
    23. }
    24. }

    ⑥UserMapper.java

    1. package com.itheima.mp.mapper;
    2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    3. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    4. import com.itheima.mp.domain.po.User;
    5. import org.apache.ibatis.annotations.Mapper;
    6. import org.apache.ibatis.annotations.Param;
    7. import org.apache.ibatis.annotations.Update;
    8. import java.util.List;
    9. @Mapper // 不要忘记加上@Mapper注解
    10. public interface UserMapper extends BaseMapper {
    11. @Update("UPDATE tb_user SET balance = balance - #{money} WHERE id = #{id}")
    12. void deductBalance(@Param("ew") Long id, @Param("money") Integer money);
    13. }

    http://localhost:8080/doc.html#/home

    案例2:IServiced的Lambda查询

    需求:实现一个根据复杂条件查询用户的接口,查询条件如下:

    • name:用户名关键字,可以为空
    • status:用户状态,可以为空
    • minBalance:最小余额,可以为空
    • maxBalance:最大余额,可以为空

    UserController.java

    1. import com.itheima.mp.query.UserQuery;
    2. // ... ...
    3. @ApiOperation("根据复杂条件查询用户接口")
    4. @GetMapping("/list")
    5. public List queryUsers(UserQuery query) {
    6. // 1. 查询用户PO
    7. List users = userService.queryUsers(
    8. query.getName(), query.getStatus(), query.getMinBalance(), query.getMaxBalance());
    9. // 2. 把PO拷贝到VO
    10. return BeanUtil.copyToList(users, UserVO.class);
    11. }

    UserQuery.java

    1. package com.itheima.mp.query;
    2. import io.swagger.annotations.ApiModel;
    3. import io.swagger.annotations.ApiModelProperty;
    4. import lombok.Data;
    5. @Data
    6. @ApiModel(description = "用户查询条件实体")
    7. public class UserQuery {
    8. @ApiModelProperty("用户名关键字")
    9. private String name;
    10. @ApiModelProperty("用户状态:1-正常,2-冻结")
    11. private Integer status;
    12. @ApiModelProperty("余额最小值")
    13. private Integer minBalance;
    14. @ApiModelProperty("余额最大值")
    15. private Integer maxBalance;
    16. }

    IUserService.java

    1. package com.itheima.mp.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.itheima.mp.domain.po.User;
    4. import java.util.List;
    5. public interface IUserService extends IService {
    6. List queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance);
    7. }

    UserServiceImpl.java

    1. @Override
    2. public List queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {
    3. return lambdaQuery()
    4. .like(name != null, User::getUsername, name)
    5. .eq(status != null, User::getStatus, status)
    6. .ge(minBalance != null, User::getBalance, minBalance)
    7. .le(maxBalance != null, User::getBalance, maxBalance)
    8. .list();
    9. }

    案例2:IService的Lambda更新

    需求:改造根据id修改用户余额的接口,要求如下

    ①完成对用户状态校验

    ②完成对用户余额校验

    ③如果扣减后余额为0,则将用户status修改为冻结状态

    UserServiceImpl.java

    1. @Override
    2. @Transactional
    3. public void deductBalance(Long id, Integer money) {
    4. // 1. 查询用户
    5. User user = getById(id);
    6. // 2. 校验用户状态
    7. if(user == null || user.getStatus() == 2) {
    8. throw new RuntimeException("用户状态异常!");
    9. }
    10. // 3. 校验用户余额是否充足
    11. if(user.getBalance() < money) {
    12. throw new RuntimeException("用户余额不足!");
    13. }
    14. // 4. 扣减余额
    15. // baseMapper.deductBalance(id, money);
    16. int remainBalance = user.getBalance() - money;
    17. lambdaUpdate()
    18. .set(User::getBalance, remainBalance)
    19. .set(remainBalance == 0, User::getStatus, 2)
    20. .eq(User::getId, id)
    21. .eq(User::getBalance, user.getBalance()) // 乐观锁
    22. .update();
    23. }

    案例3:IService批量新增

    需求:批量插入1万条用户数据,并作出对比:

    • 普通for循环插入
    • IService的批量插入
    1. private User buildUser(int i) {
    2. User user = new User();
    3. user.setUsername("user_" + i);
    4. user.setPassword("123");
    5. user.setPhone("" + (18688190000L + i));
    6. user.setBalance(2000);
    7. user.setInfo("{\"age\": 24, \"intro\": \"语文老师\", \"gender\": \"female\"}");
    8. user.setCreateTime(LocalDateTime.now());
    9. user.setUpdateTime(LocalDateTime.now());
    10. return user;
    11. }
    12. @Test
    13. void testSaveOneByOne() {
    14. long b = System.currentTimeMillis();
    15. for (int i = 1; i <= 100000; i++) {
    16. userService.save(buildUser(i));
    17. }
    18. long e = System.currentTimeMillis();
    19. System.out.println("耗时:" + (e - b));
    20. }
    21. @Test
    22. void testSaveBatch() {
    23. // 我们每次批量插入1000条件,插入100次,即10万条数据
    24. // 1. 准备一个容量为1000的集合
    25. List list = new ArrayList<>(1000);
    26. long b = System.currentTimeMillis();
    27. for (int i = 1; i <= 100000; i++) {
    28. // 2. 添加一个user
    29. list.add(buildUser(i));
    30. // 3. 每隔1000条批量插入一次
    31. if(i % 1000 == 0) {
    32. userService.saveBatch(list);
    33. // 4. 清空集合,准备下一批数据
    34. list.clear();
    35. }
    36. }
    37. long e = System.currentTimeMillis();
    38. System.out.println("耗时:" + (e - b));
    39. }

    测试完记得删除测试数据

    开启rewriteBatchedStatements=true参数  - application.yaml

    1. spring:
    2. datasource:
    3. url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
    4. driver-class-name: com.mysql.cj.jdbc.Driver
    5. username: root
    6. password: 123456

    3. 扩展功能

    3.1 代码生成

    代码生成器 | MyBatis-Plus

    Mybatis X 插件 | MyBatis-Plus

    MyBatisPlus插件装不了的直接进官网安装:MyBatisPlus - IntelliJ IDEs Plugin | Marketplace

    ①测试连接

    ②代码生成

    注:在AddressServiceImpl里加上@Service注解

    效果:

    3.2 静态工具

    案例1:静态工具查询

    需求:

    • ①改造根据id查询用户的接口,查询用户的同时,查询除用户对应的所有地址
    • ②改造根据id批量查询用户的接口,查询用户的同时,查询出用户对应的所有地址

    (1)改造根据id查询用户的接口,查询用户的同时,查询除用户对应的所有地址

    出现service相互调用的情况。

    ①从Day01的资料里拷贝AddressVO.java到vo文件夹下,在UserVO.java中添加地址属性:

    1. @ApiModelProperty("用户的收货地址")
    2. private List addresses;

    ②改写UserController.java下的 queryUserById方法

    1. @ApiOperation("根据id查询用户接口")
    2. @GetMapping("{id}")
    3. public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long id) {
    4. return userService.queryUserAndAddressById(id);
    5. }

    ③IUserService.java

    UserVO queryUserAndAddressById(Long id);

    ④UserServiceImpl.java

    1. import cn.hutool.core.bean.BeanUtil;
    2. import cn.hutool.core.collection.CollUtil;
    3. import com.baomidou.mybatisplus.extension.toolkit.Db;
    4. // ... ...
    5. @Override
    6. public UserVO queryUserAndAddressById(Long id) {
    7. // 1. 查询用户
    8. User user = getById(id);
    9. if(user == null || user.getStatus() == 2) {
    10. throw new RuntimeException("用户状态异常!");
    11. }
    12. // 2. 查询地址
    13. List
      addresses = Db.lambdaQuery(Address.class).eq(Address::getUserId, id).list();
    14. // 3. 封装VO
    15. // 3.1 转User的PO为VO
    16. UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
    17. // 3.2 转地址VO
    18. if(CollUtil.isNotEmpty(addresses)) {
    19. userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
    20. }
    21. return userVO;
    22. }

    (2)改造根据id批量查询用户的接口,查询用户的同时,查询出用户对应的所有地址

    ①UserController.java

    1. @ApiOperation("根据id批量查询用户接口")
    2. @GetMapping
    3. public List queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List ids) {
    4. return userService.queryUserAndAddressByIds(ids);
    5. }

    ②IUserService.java

    List queryUserAndAddressByIds(List ids);

    ③UserServiceImpl.java

    1. @Override
    2. public List queryUserAndAddressByIds(List ids) {
    3. // 1. 查询用户
    4. List users = listByIds(ids);
    5. if(CollUtil.isEmpty(users)) {
    6. return Collections.emptyList();
    7. }
    8. // 2. 查询地址
    9. // 2.1 获取用户id集合
    10. List userIds = users.stream().map(User::getId).collect(Collectors.toList());
    11. // 2.2 根据用户id查询地址
    12. List
      addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
    13. // 2.3 转换地址vo
    14. List addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);
    15. // 2.4 用户地址集合分组处理,相同用户的放入一个集合(组)中
    16. Map> addressMap = new HashMap<>(0);
    17. if(CollUtil.isNotEmpty(addressVOList)) {
    18. addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
    19. }
    20. // 3. 转VO返回
    21. List list = new ArrayList<>(users.size());
    22. for (User user : users) {
    23. // 3.1 转换user的po为vo
    24. UserVO vo = BeanUtil.copyProperties(user, UserVO.class);
    25. list.add(vo);
    26. // 3.2 转换地址vo
    27. vo.setAddresses(addressMap.get(user.getId()));
    28. }
    29. return list;
    30. }

    3.3 逻辑删除

    逻辑删除就是基于代码逻辑模拟删除效果,但并不会真正删除数据。思路如下:

    • 在表中添加一个字段标记数据是否被删除
    • 当删除数据时把标记置为1
    • 查询时只查询标记为0的数据

    例如,逻辑删除字段为deleted:

    • 删除操作:
    UPDATE user SET deleted = 1 WHERE id = 1 AND deleted = 0
    • 查询操作:
    SELECT * FROM user WHERE deleted = 0

    MyBatisPlus提供了逻辑删除功能,无需改变方法调应的方式,而是在底层帮我们自动修改CRUD的语句。我们要做的就是在application.yaml文件中配置逻辑删除的字段名称和值即可:

    1. mybatis-plus:
    2. global-config:
    3. db-config:
    4. id-type: auto
    5. logic-delete-field: deleted # 全局逻辑删除的实体字段名,字段类型可以是booleaninteger
    6. logic-delete-value: 1 # 逻辑已删除值(默认为1
    7. logic-not-delete-value: 0 # 逻辑未删除值(默认为0

    IAddressServiceTest.java

    1. package com.itheima.mp.service;
    2. import com.itheima.mp.domain.po.Address;
    3. import org.junit.jupiter.api.Test;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.boot.test.context.SpringBootTest;
    6. import static org.junit.jupiter.api.Assertions.*;
    7. @SpringBootTest
    8. class IAddressServiceTest {
    9. @Autowired
    10. private IAddressService addressService;
    11. @Test
    12. void testLogicDelete() {
    13. // 1. 删除
    14. addressService.removeById(59L);
    15. // 2. 查询
    16. Address address = addressService.getById(59L);
    17. System.out.println("address = " + address); // null
    18. }
    19. }

    注意:逻辑删除本身也有自己的问题,比如:

    • 会导致数据库表垃圾数据越来越多,影响查询效率
    • SQL中全都需要对逻辑删除字段做判断,影响查询效率

    因此,不太推荐采用逻辑删除功能,如果数据不能删除,可以采用把数据迁移到其他表的方法。

    3.4 枚举处理器

    User类中有一个用户状态字段:

    通用枚举

    在application.yaml中配置全局枚举处理器,实现类型转换:

    1. mybatis-plus:
    2. type-aliases-package: com.itheima.mp.domain.po
    3. configuration:
    4. default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

    UserStatus.java

    1. package com.itheima.mp.enums;
    2. import com.baomidou.mybatisplus.annotation.EnumValue;
    3. import com.fasterxml.jackson.annotation.JsonValue;
    4. import lombok.Getter;
    5. @Getter
    6. public enum UserStatus {
    7. NORMAL(1, "正常"),
    8. FROZEN(2, "冻结"),
    9. ;
    10. @EnumValue
    11. private final int value;
    12. @JsonValue
    13. private final String desc;
    14. UserStatus(int value, String desc) {
    15. this.value = value;
    16. this.desc = desc;
    17. }
    18. }

    User.java

    1. import com.itheima.mp.enums.UserStatus;
    2. /**
    3. * 账户余额
    4. */
    5. private UserStatus balance;

    UserVO.java

    1. @ApiModelProperty("使用状态(1正常 2冻结)")
    2. private UserStatus status;

    把用到账户状态值的地方改成枚举变量 - UserServiceImpl.java

    1. package com.itheima.mp.service.impl;
    2. import cn.hutool.core.bean.BeanUtil;
    3. import cn.hutool.core.collection.CollUtil;
    4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    5. import com.baomidou.mybatisplus.extension.toolkit.Db;
    6. import com.itheima.mp.domain.po.Address;
    7. import com.itheima.mp.domain.po.User;
    8. import com.itheima.mp.domain.vo.AddressVO;
    9. import com.itheima.mp.domain.vo.UserVO;
    10. import com.itheima.mp.enums.UserStatus;
    11. import com.itheima.mp.mapper.UserMapper;
    12. import com.itheima.mp.service.IUserService;
    13. import org.springframework.stereotype.Service;
    14. import org.springframework.transaction.annotation.Transactional;
    15. import java.util.*;
    16. import java.util.stream.Collectors;
    17. @Service
    18. public class UserServiceImpl extends ServiceImpl implements IUserService {
    19. @Override
    20. @Transactional
    21. public void deductBalance(Long id, Integer money) {
    22. // 1. 查询用户
    23. User user = getById(id);
    24. // 2. 校验用户状态
    25. if (user == null || user.getStatus() == UserStatus.FROZEN) {
    26. throw new RuntimeException("用户状态异常!");
    27. }
    28. // 3. 校验用户余额是否充足
    29. if (user.getBalance() < money) {
    30. throw new RuntimeException("用户余额不足!");
    31. }
    32. // 4. 扣减余额
    33. // baseMapper.deductBalance(id, money);
    34. int remainBalance = user.getBalance() - money;
    35. lambdaUpdate()
    36. .set(User::getBalance, remainBalance)
    37. .set(remainBalance == 0, User::getStatus, UserStatus.FROZEN)
    38. .eq(User::getId, id)
    39. .eq(User::getBalance, user.getBalance()) // 乐观锁
    40. .update();
    41. }
    42. @Override
    43. public List queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {
    44. return lambdaQuery()
    45. .like(name != null, User::getUsername, name)
    46. .eq(status != null, User::getStatus, status)
    47. .ge(minBalance != null, User::getBalance, minBalance)
    48. .le(maxBalance != null, User::getBalance, maxBalance)
    49. .list();
    50. }
    51. @Override
    52. public UserVO queryUserAndAddressById(Long id) {
    53. // 1. 查询用户
    54. User user = getById(id);
    55. if(user == null || user.getStatus() == UserStatus.FROZEN) {
    56. throw new RuntimeException("用户状态异常!");
    57. }
    58. // 2. 查询地址
    59. List
      addresses = Db.lambdaQuery(Address.class).eq(Address::getUserId, id).list();
    60. // 3. 封装VO
    61. // 3.1 转User的PO为VO
    62. UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
    63. // 3.2 转地址VO
    64. if(CollUtil.isNotEmpty(addresses)) {
    65. userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
    66. }
    67. return userVO;
    68. }
    69. @Override
    70. public List queryUserAndAddressByIds(List ids) {
    71. // 1. 查询用户
    72. List users = listByIds(ids);
    73. if(CollUtil.isEmpty(users)) {
    74. return Collections.emptyList();
    75. }
    76. // 2. 查询地址
    77. // 2.1 获取用户id集合
    78. List userIds = users.stream().map(User::getId).collect(Collectors.toList());
    79. // 2.2 根据用户id查询地址
    80. List
      addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
    81. // 2.3 转换地址vo
    82. List addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);
    83. // 2.4 用户地址集合分组处理,相同用户的放入一个集合(组)中
    84. Map> addressMap = new HashMap<>(0);
    85. if(CollUtil.isNotEmpty(addressVOList)) {
    86. addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
    87. }
    88. // 3. 转VO返回
    89. List list = new ArrayList<>(users.size());
    90. for (User user : users) {
    91. // 3.1 转换user的po为vo
    92. UserVO vo = BeanUtil.copyProperties(user, UserVO.class);
    93. list.add(vo);
    94. // 3.2 转换地址vo
    95. vo.setAddresses(addressMap.get(user.getId()));
    96. }
    97. return list;
    98. }
    99. }

    总结:

    如何实现PO类中的枚举类型变量与数据库字段的转换?

    ①给枚举中的与数据库对应value值添加@EnumValue注解

    ②在配置文件中配置统一的枚举处理器,实现类型转换。

    3.5 JSON处理器

    ①userInfo.java

    1. package com.itheima.mp.domain.po;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Data;
    4. import lombok.NoArgsConstructor;
    5. @Data
    6. @NoArgsConstructor
    7. @AllArgsConstructor(staticName = "of")
    8. public class UserInfo {
    9. private Integer age;
    10. private String intro;
    11. private String gender;
    12. }

    ②User.java

    1. package com.itheima.mp.domain.po;
    2. import com.baomidou.mybatisplus.annotation.IdType;
    3. import com.baomidou.mybatisplus.annotation.TableField;
    4. import com.baomidou.mybatisplus.annotation.TableId;
    5. import com.baomidou.mybatisplus.annotation.TableName;
    6. import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
    7. import com.itheima.mp.enums.UserStatus;
    8. import lombok.Data;
    9. import java.time.LocalDateTime;
    10. @Data
    11. @TableName(value = "tb_user", autoResultMap = true)
    12. public class User {
    13. // ... ...
    14. /**
    15. * 详细信息
    16. */
    17. @TableField(typeHandler = JacksonTypeHandler.class)
    18. private UserInfo info;
    19. // ... ...
    20. }

    ③设置用户信息改成

    1. // user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
    2. user.setInfo(UserInfo.of(24,"英文老师","female"));

    ④UserVO.java

    1. @ApiModelProperty("详细信息")
    2. private UserInfo info;

    ⑤UserFormDTO.java

    1. @ApiModelProperty("详细信息,JSON风格")
    2. private UserInfo info;

    如果有出现下面的问题,把UserMapper.xml里的SQL语句改成如下,删除parameterType="com.itheima.mp.domain.po.User"

    1. <insert id="saveUser">
    2. INSERT INTO `tb_user` (`id`, `username`, `password`, `phone`, `info`, `balance`)
    3. VALUES (#{id}, #{username}, #{password}, #{phone}, #{info}, #{balance});
    4. insert>

    4. 插件功能

    MyBatisPlus提供的内置拦截器有下面这些:

    拦截器描述
    TenantLineInnerInterceptor多租户插件
    DynamicTableNameInnerInterceptor动态表名插件
    PaginationInnerInterceptor分页插件
    OptimisticLockerInnerInterceptor乐观锁插件
    IllegalSQLInnerInterceptorSQL性能规范插件,检测并拦截垃圾SQL
    BlockAttackInnerInterceptor防止全表更新和删除的插件

    4.1 分页插件

    ①在配置类中注册MyBatisPlus的核心插件,同时添加分页插件:

    1. package com.itheima.mp.config;
    2. import com.baomidou.mybatisplus.annotation.DbType;
    3. import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    4. import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    5. import org.springframework.context.annotation.Bean;
    6. import org.springframework.context.annotation.Configuration;
    7. @Configuration
    8. public class MyBatisConfig {
    9. @Bean
    10. public MybatisPlusInterceptor mybatisPlusInterceptor() {
    11. MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    12. // 1. 创建分页插件
    13. PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
    14. paginationInnerInterceptor.setMaxLimit(1000L); // 设置分页上限
    15. // 2. 添加分页插件
    16. interceptor.addInnerInterceptor(paginationInnerInterceptor);
    17. return interceptor;
    18. }
    19. }

    ②使用分页的API

    IUserServiceTest.java

    1. @Test
    2. void testPageQuery() {
    3. int pageNo = 1, pageSize = 2;
    4. // 1. 准备分页条件
    5. // 1.1 分页条件
    6. Page page = Page.of(pageNo, pageSize);
    7. // 1.2 排序条件
    8. page.addOrder(new OrderItem("balance", true));
    9. page.addOrder(new OrderItem("id", true));
    10. // 2. 分页查询
    11. Page p = userService.page(page);
    12. // 3. 解析
    13. long total = p.getTotal();
    14. System.out.println("total = " + total);
    15. long pages = p.getPages();
    16. System.out.println("pages = " + pages);
    17. List users = p.getRecords();
    18. users.forEach(System.out::println);
    19. }

    4.2 通用分页实体

    案例1:简单分页查询案例

    需求:遵循下面的接口规范,编写一个UserController接口,实现User的分页查询

    ①PageQuery.java

    1. package com.itheima.mp.query;
    2. import io.swagger.annotations.ApiModel;
    3. import io.swagger.annotations.ApiModelProperty;
    4. import lombok.Data;
    5. @Data
    6. @ApiModel(description = "分页查询实体")
    7. public class PageQuery {
    8. @ApiModelProperty("页码")
    9. private Integer pageNo;
    10. @ApiModelProperty("页大小")
    11. private Integer pageSize;
    12. @ApiModelProperty("排序字段")
    13. private String sortBy;
    14. @ApiModelProperty("是否升序")
    15. private Boolean isAsc;
    16. }

    ②UserQuery继承PageQuery

    1. package com.itheima.mp.query;
    2. import io.swagger.annotations.ApiModel;
    3. import io.swagger.annotations.ApiModelProperty;
    4. import lombok.Data;
    5. import lombok.EqualsAndHashCode;
    6. @EqualsAndHashCode(callSuper = true)
    7. @Data
    8. @ApiModel(description = "用户查询条件实体")
    9. public class UserQuery extends PageQuery{
    10. @ApiModelProperty("用户名关键字")
    11. private String name;
    12. @ApiModelProperty("用户状态:1-正常,2-冻结")
    13. private Integer status;
    14. @ApiModelProperty("余额最小值")
    15. private Integer minBalance;
    16. @ApiModelProperty("余额最大值")
    17. private Integer maxBalance;
    18. }

    ③PageDTO.java

    1. package com.itheima.mp.domain.dto;
    2. import io.swagger.annotations.ApiModel;
    3. import io.swagger.annotations.ApiModelProperty;
    4. import lombok.Data;
    5. import java.util.List;
    6. @Data
    7. @ApiModel(description = "分页结果")
    8. public class PageDTO {
    9. @ApiModelProperty("总条数")
    10. private Long total;
    11. @ApiModelProperty("总页数")
    12. private Long pages;
    13. @ApiModelProperty("集合")
    14. private List list;
    15. }

    ④UserController.java

    1. @ApiOperation("根据复杂条件分页查询用户接口")
    2. @GetMapping("/page")
    3. public PageDTO queryUsersPage(UserQuery query) {
    4. return userService.queryUsersPage(query);
    5. }

    ⑤IUserService.java

    PageDTO queryUsersPage(UserQuery query);

    ⑥UserSeerviceImpl.java

    1. package com.itheima.mp.service.impl;
    2. import cn.hutool.core.bean.BeanUtil;
    3. import cn.hutool.core.collection.CollUtil;
    4. import cn.hutool.core.util.StrUtil;
    5. import com.baomidou.mybatisplus.core.metadata.OrderItem;
    6. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    7. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    8. import com.baomidou.mybatisplus.extension.toolkit.Db;
    9. import com.itheima.mp.domain.dto.PageDTO;
    10. import com.itheima.mp.domain.po.Address;
    11. import com.itheima.mp.domain.po.User;
    12. import com.itheima.mp.domain.vo.AddressVO;
    13. import com.itheima.mp.domain.vo.UserVO;
    14. import com.itheima.mp.enums.UserStatus;
    15. import com.itheima.mp.mapper.UserMapper;
    16. import com.itheima.mp.query.UserQuery;
    17. import com.itheima.mp.service.IUserService;
    18. import org.springframework.stereotype.Service;
    19. import java.util.*;
    20. import java.util.stream.Collectors;
    21. @Service
    22. public class UserServiceImpl extends ServiceImpl implements IUserService {
    23. // ... ...
    24. @Override
    25. public PageDTO queryUsersPage(UserQuery query) {
    26. String name = query.getName();
    27. Integer status = query.getStatus();
    28. Integer minBalance = query.getMinBalance();
    29. Integer maxBalance = query.getMaxBalance();
    30. // 1. 构建查询条件
    31. // 1.1 分页条件
    32. Page page = Page.of(query.getPageNo(), query.getPageSize());
    33. // 1.2 排序条件
    34. if(StrUtil.isNotBlank(query.getSortBy())) {
    35. // 排序字段不为空,按照排序字段排序
    36. page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));
    37. } else {
    38. // 排序字段为空,默认按照更新时间排序
    39. page.addOrder(new OrderItem("update_time", false));
    40. }
    41. // 2. 分页查询
    42. Page p = lambdaQuery()
    43. .like(name != null, User::getUsername, name)
    44. .eq(status != null, User::getStatus, status)
    45. .ge(minBalance != null, User::getBalance, minBalance)
    46. .le(maxBalance != null, User::getBalance, maxBalance)
    47. .page(page);
    48. // 3. 封装VO结果
    49. PageDTO dto = new PageDTO<>();
    50. // 3.1 总条数
    51. dto.setTotal(p.getTotal());
    52. // 3.2 总页数
    53. dto.setPages(p.getPages());
    54. // 3.3 当前页数据
    55. List records = p.getRecords();
    56. if(CollUtil.isEmpty(records)) {
    57. dto.setList(Collections.emptyList());
    58. return dto;
    59. }
    60. // 3.4 拷贝user的VO
    61. dto.setList(BeanUtil.copyToList(records, UserVO.class));
    62. // 4. 返回
    63. return dto;
    64. }
    65. }

    案例2:通用分页实体

    需求:

    • 在PageQuery中定义方法,将PageQuery对象转为MyBatisPlus中的page对象
    • 在PageDTO中定义方法,将MyBatisPlus中的Page结果转为PageDTO结果

    ①PageQuery.java

    1. package com.itheima.mp.query;
    2. import cn.hutool.core.util.StrUtil;
    3. import com.baomidou.mybatisplus.core.metadata.OrderItem;
    4. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    5. import com.itheima.mp.domain.po.User;
    6. import io.swagger.annotations.ApiModel;
    7. import io.swagger.annotations.ApiModelProperty;
    8. import lombok.Data;
    9. @Data
    10. @ApiModel(description = "分页查询实体")
    11. public class PageQuery {
    12. @ApiModelProperty("页码")
    13. private Integer pageNo = 1;
    14. @ApiModelProperty("页大小")
    15. private Integer pageSize = 5;
    16. @ApiModelProperty("排序字段")
    17. private String sortBy;
    18. @ApiModelProperty("是否升序")
    19. private Boolean isAsc = true;
    20. public Page toMpPage(OrderItem ... items) {
    21. // 1 分页条件
    22. Page page = Page.of(pageNo, pageSize);
    23. // 2 排序条件
    24. if(StrUtil.isNotBlank(sortBy)) {
    25. // 排序字段不为空,按照排序字段排序
    26. page.addOrder(new OrderItem(sortBy, isAsc));
    27. } else if(items != null) {
    28. // 排序字段为空,默认排序
    29. page.addOrder(items);
    30. }
    31. return page;
    32. }
    33. public Page toMpPage(String defaultSortBy, Boolean defaultAsc) {
    34. return toMpPage(new OrderItem(defaultSortBy, defaultAsc));
    35. }
    36. public Page toMpPageDefaultSortByCreateTime() {
    37. return toMpPage(new OrderItem("create_time", false));
    38. }
    39. public Page toMpPageDefaultSortByUpdateTime() {
    40. return toMpPage(new OrderItem("update_time", false));
    41. }
    42. }

    ②PageDTO.java

    1. package com.itheima.mp.domain.dto;
    2. import cn.hutool.core.bean.BeanUtil;
    3. import cn.hutool.core.collection.CollUtil;
    4. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    5. import com.itheima.mp.domain.po.User;
    6. import com.itheima.mp.domain.vo.UserVO;
    7. import io.swagger.annotations.ApiModel;
    8. import io.swagger.annotations.ApiModelProperty;
    9. import lombok.Data;
    10. import java.util.Collections;
    11. import java.util.List;
    12. import java.util.function.Function;
    13. import java.util.stream.Collectors;
    14. @Data
    15. @ApiModel(description = "分页结果")
    16. public class PageDTO {
    17. @ApiModelProperty("总条数")
    18. private Long total;
    19. @ApiModelProperty("总页数")
    20. private Long pages;
    21. @ApiModelProperty("集合")
    22. private List list;
    23. public static PageDTO of(Page p, Class clazz) {
    24. PageDTO dto = new PageDTO<>();
    25. // 1 总条数
    26. dto.setTotal(p.getTotal());
    27. // 2 总页数
    28. dto.setPages(p.getPages());
    29. // 3 当前页数据
    30. List records = p.getRecords();
    31. if(CollUtil.isEmpty(records)) {
    32. dto.setList(Collections.emptyList());
    33. return dto;
    34. }
    35. // 4 拷贝user的VO
    36. dto.setList(BeanUtil.copyToList(records, clazz));
    37. // 5. 返回
    38. return dto;
    39. }
    40. public static PageDTO of(Page p, Function convertor) {
    41. PageDTO dto = new PageDTO<>();
    42. // 1 总条数
    43. dto.setTotal(p.getTotal());
    44. // 2 总页数
    45. dto.setPages(p.getPages());
    46. // 3 当前页数据
    47. List records = p.getRecords();
    48. if(CollUtil.isEmpty(records)) {
    49. dto.setList(Collections.emptyList());
    50. return dto;
    51. }
    52. // 4 拷贝user的VO
    53. dto.setList(records.stream().map(convertor).collect(Collectors.toList()));
    54. // 5. 返回
    55. return dto;
    56. }
    57. }

    ③UserServiceImpl.java

    1. package com.itheima.mp.service.impl;
    2. import cn.hutool.core.bean.BeanUtil;
    3. import cn.hutool.core.collection.CollUtil;
    4. import cn.hutool.core.util.StrUtil;
    5. import com.baomidou.mybatisplus.core.metadata.OrderItem;
    6. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    7. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    8. import com.baomidou.mybatisplus.extension.toolkit.Db;
    9. import com.itheima.mp.domain.dto.PageDTO;
    10. import com.itheima.mp.domain.po.Address;
    11. import com.itheima.mp.domain.po.User;
    12. import com.itheima.mp.domain.vo.AddressVO;
    13. import com.itheima.mp.domain.vo.UserVO;
    14. import com.itheima.mp.enums.UserStatus;
    15. import com.itheima.mp.mapper.UserMapper;
    16. import com.itheima.mp.query.UserQuery;
    17. import com.itheima.mp.service.IUserService;
    18. import org.springframework.stereotype.Service;
    19. import org.springframework.transaction.annotation.Transactional;
    20. import java.util.*;
    21. import java.util.stream.Collectors;
    22. @Service
    23. public class UserServiceImpl extends ServiceImpl implements IUserService {
    24. @Override
    25. public List queryUserAndAddressByIds(List ids) {
    26. // 1. 查询用户
    27. List users = listByIds(ids);
    28. if(CollUtil.isEmpty(users)) {
    29. return Collections.emptyList();
    30. }
    31. // 2. 查询地址
    32. // 2.1 获取用户id集合
    33. List userIds = users.stream().map(User::getId).collect(Collectors.toList());
    34. // 2.2 根据用户id查询地址
    35. List
      addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
    36. // 2.3 转换地址vo
    37. List addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);
    38. // 2.4 用户地址集合分组处理,相同用户的放入一个集合(组)中
    39. Map> addressMap = new HashMap<>(0);
    40. if(CollUtil.isNotEmpty(addressVOList)) {
    41. addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
    42. }
    43. // 3. 转VO返回
    44. List list = new ArrayList<>(users.size());
    45. for (User user : users) {
    46. // 3.1 转换user的po为vo
    47. UserVO vo = BeanUtil.copyProperties(user, UserVO.class);
    48. list.add(vo);
    49. // 3.2 转换地址vo
    50. vo.setAddresses(addressMap.get(user.getId()));
    51. }
    52. return list;
    53. }
    54. @Override
    55. public PageDTO queryUsersPage(UserQuery query) {
    56. String name = query.getName();
    57. Integer status = query.getStatus();
    58. Integer minBalance = query.getMinBalance();
    59. Integer maxBalance = query.getMaxBalance();
    60. // 1. 构建查询条件
    61. Page page = query.toMpPageDefaultSortByUpdateTime();
    62. // 2. 分页查询
    63. Page p = lambdaQuery()
    64. .like(name != null, User::getUsername, name)
    65. .eq(status != null, User::getStatus, status)
    66. .ge(minBalance != null, User::getBalance, minBalance)
    67. .le(maxBalance != null, User::getBalance, maxBalance)
    68. .page(page);
    69. // 3. 封装VO结果
    70. // return PageDTO.of(p, UserVO.class);
    71. // return PageDTO.of(p, user -> BeanUtil.copyProperties(user, UserVO.class));
    72. return PageDTO.of(p, user -> {
    73. // 1. 拷贝基础属性
    74. UserVO vo = BeanUtil.copyProperties(user, UserVO.class);
    75. // 2. 处理特殊逻辑
    76. vo.setUsername(vo.getUsername().substring(0, vo.getUsername().length() - 2) + "**");
    77. return vo;
    78. });
    79. }
    80. }

  • 相关阅读:
    leetcode:66. 加一(python3解法)
    vue3父子组件传值,子组件暴漏方法
    一同走进Linux的“基操”世界
    Spring面试(源码手撕)
    LeetCode刷题 309 :最佳买卖股票的时机含冷冻时期
    Docker【部署 05】docker使用tensorflow-gpu安装及调用GPU踩坑记录
    Web服务器与Http协议
    《向量数据库指南》——完善产品拼图,GBASE南大通用发布向量数据库
    【技术积累】Python中的PyTorch库【一】
    transition和animation的区别?
  • 原文地址:https://blog.csdn.net/ltt159264/article/details/139393401