MybatisPlus使用指南

news2024/11/24 4:36:41

MybatisPlus

  • 1. 快速入门
    • 1.1 入门案例
    • 1.2 常见注解
    • 1.3 常见配置
  • 2. 核心功能
    • 2.1 条件构造器
    • 2.2 自定义SQL
    • 2.3 Service接口
  • 3. 扩展功能
    • 3.1 代码生成
    • 3.2 静态工具
    • 3.3 逻辑删除
  • 4. 插件功能
    • 4.1 分页插件
    • 4.2 通用分页实体

在这里插入图片描述

1. 快速入门

1.1 入门案例

步骤一:引入MybatisPlus的起步依赖
MyBatisPlus官方提供了starter,其中集成了Mybatis和MybatisPlus的所有功能,并且实现了自动装配效果。
因此我们可以用MybatisPlus的starter代替Mybatis的starter:

 <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
      <version>3.5.3.1</version>
</dependency>

步骤二:定义Mapper继承MybaitsPlus提供的BaseMapper接口

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

注:BaseMapper中已经定义了常用的CURD方法

在这里插入图片描述

步骤三:调用接口中的方法进行测试

在这里插入图片描述在这里插入图片描述

1.2 常见注解

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

在这里插入图片描述

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

  1. @TableName: 用来指定表明
  2. @TableId: 用来指定表中的主键字段信息
  3. @TableField: 用来指定表中的普通字段信息

在这里插入图片描述

IdType枚举

  • AUTO: 数据库自增长
  • INPUT: 通过set方法自行输入
  • ASSIGN_ID: 分配ID,接口IdentifierGenerator的方法nextId来生成id,默认实现类为DefaultIdentifierGenerator雪花算法

使用@TableFidle的常见场景

  • 成员变量名与数据库字段名不一致
  • 成员变量名以is开头,且是布尔值
  • 成员变量名与数据库关键字冲突
  • 成员变量不是数据库字段

1.3 常见配置

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

mybatis-plus:
  mapper-locations: classpath*:mapper/**/*.xml # Mapper.xml文件地址,默认值
  type-aliases-package: com.itheima.mp.domain.po # 别名扫描包
  configuration:
    map-underscore-to-camel-case: true  # 是否开启下户线和驼峰的映射
    cache-enabled: false # 是否开启二级缓存
  global-config:
    db-config:
      id-type: assign_id  # id为雪花算法生成
      update-strategy: not_null # 更新策略

2. 核心功能

2.1 条件构造器

刚才的案例中都是以id为条件的简单CRUD,一些复杂条件的SQL语句就要用到一些更高级的功能了。

在这里插入图片描述

参数中的Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图:

在这里插入图片描述

Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法:

在这里插入图片描述

而QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段

在这里插入图片描述

而UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分

在这里插入图片描述

QueryWrapper:无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件。查询:查询出名字中带o的,存款大于等于1000元的人。代码如下:

@SpringBootTest
public class UserWrapperTest {

    @Resource
    UserMapper userMapper;

    @Test
    void testQueryWrapper(){
        // 1.构建查询条件 where name like "%o%" AND balance >= 1000
        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .select("id", "username", "info", "balance")
                .like("username", "o")
                .ge("balance", 1000);
        // 2. 查询数据
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }

UpdateWrapper

更新用户名为jack的用户的余额为2000,代码如下:

@SpringBootTest
public class UserWrapperTest {

    @Resource
    UserMapper userMapper;

    @Test
    void testUpdateWrapper(){
        // 1.构建查询条件 set balance = 20000 where name = "Jack"
        UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
                .setSql("balance = 2000")
                .eq("username", "jack");
        // 2. 查询数据
        int update = userMapper.update(null, wrapper);
        System.out.println(update);
    }
}

更新id为1,2,4的用户的余额,扣200,代码如下:

@SpringBootTest
public class UserWrapperTest {
    @Resource
    UserMapper userMapper;

    @Test
    void testUpdateBatchWrapper(){
        // 1.构建查询条件
        UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
                .setSql("balance = balance - 200")  // SET balance = balance - 200
                .in("id",List.of(1L, 2L, 4L)); // // WHERE id in (1, 2, 4)
        // 2. 查询数据,注意第一个参数可以给null,也就是不填更新字段和数据,而是基于UpdateWrapper中的setSQL来更新
        int update = userMapper.update(null, wrapper);
        System.out.println(update);
    }

}

无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值。这在编程规范中显然是不推荐的。那怎么样才能不写字段名,又能知道字段名呢?其中一种办法是基于变量的gettter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用和Lambda表达式。
因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:

  • LambdaQueryWrapper
  • LambdaUpdateWrapper

分别对应QueryWrapper和UpdateWrapper

@SpringBootTest
public class UserWrapperTest {

    @Resource
    UserMapper userMapper;

    @Test
    void testLambdaQueryWrapper(){
        // 1.构建查询条件 where name like "%o%" AND balance >= 1000
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .select(User::getId, User::getUsername, User::getInfo, User::getBalance)
                .like(User::getUsername, "o")
                .ge(User::getBalance, 1000);
        // 2. 查询数据
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }
    
    @Test
    void testLambdaUpdateWrapper(){
        // 1.构建查询条件
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>()
                .setSql("balance = 2000")
                .eq(User::getUsername, "jack");
        // 2. 查询数据
        int update = userMapper.update(null, wrapper);
        System.out.println(update);
    }
}

2.2 自定义SQL

在演示UpdateWrapper的案例中,我们在代码中编写了更新的SQL语句:

 @Test
    void testUpdateBatchWrapper(){
        // 1.构建查询条件
        UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
                .setSql("balance = balance - 200")  // SET balance = balance - 200
                .in("id",List.of(1L, 2L, 4L)); // // WHERE id in (1, 2, 4)
        // 2. 查询数据,注意第一个参数可以给null,也就是不填更新字段和数据,而是基于UpdateWrapper中的setSQL来更新
        int update = userMapper.update(null, wrapper);
        System.out.println(update);
    }

这种写法在某些企业也是不允许的,因为SQL语句最好都维护在持久层,而不是业务层。MybatisPlus提供了自定义SQL功能,先利用Wrapper来构建复杂的where条件,再结合Mapper.xml或注解自己编写SQL语句。

核心思想:将where条件使用Wrapper来构建,然后在Mapper.xml进行调用即可

在这里插入图片描述

2.3 Service接口

MybatisPlus不仅提供了BaseMapper,还提供了通用的Service接口及默认实现,封装了一些常用的service模板方法。
通用接口为IService,默认实现为ServiceImpl,其中封装的方法可以分为以下几类:

  • save:新增
  • remove:删除
  • update:更新
  • get:查询单个结果
  • list:查询集合结果
  • count:计数
  • page:分页查询
    在这里插入图片描述

在这里插入图片描述

  • 自定义Service接口继承IService接口
  • 自定义Service实现类,实现自定义接口并集成ServiceImpl类

由于Service中经常需要定义与业务有关的自定义方法,因此我们不能直接使用IService,而是自定义Service接口,然后继承IService以拓展方法。同时,让自定义的Service实现类继承ServiceImpl,这样就不用自己实现IService中的接口了。
首先,定义IUserService,继承IService:

package com.itheima.mp.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;

public interface IUserService extends IService<User> {
    // 拓展自定义方法
}

然后,编写UserServiceImpl类,继承ServiceImpl,实现UserService:

package com.itheima.mp.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.po.service.IUserService;
import com.itheima.mp.mapper.UserMapper;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>                                                                                           implements IUserService {
}

接下来实现下面5个接口:

在这里插入图片描述

首先引入依赖

<!--swagger-->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
    <version>4.1.0</version>
</dependency>
<!--web-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

然后需要配置swagger信息:

knife4j:
  enable: true
  openapi:
    title: 用户管理接口文档
    description: "用户管理接口文档"
    email: xxxxx
    concat: xxxx
    url: xxxx
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources:
          - com.itheima.mp.controller

导入相关的UserVo、UserDTO、UserFormDTO(前端传入参数较多的时候封装成类)

UserController

package com.itheima.mp.controller;

import cn.hutool.core.bean.BeanUtil;
import com.itheima.mp.domain.dto.UserFormDTO;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Mr.Lu
 * @version 1.0
 * @date 2024-08-13 20:57
 */

@RequestMapping("/users")
@RestController
@Slf4j
@Api(tags = "用户管理接口")
public class UserController {
    @Resource
    private IUserService userService;

    @PostMapping
    @ApiOperation("新增用户")
    public void saveUser(@RequestBody UserFormDTO userFormDTO){
        User user = new User();
        BeanUtil.copyProperties(userFormDTO, user);
        userService.save(user);
    }

    @DeleteMapping("/{id}")
    @ApiOperation("删除用户")
    public void removeUserById(@PathVariable("id") Long userId){
        userService.removeById(userId);
    }

    @GetMapping("/{id}")
    @ApiOperation("根据id查询用户")
    public UserVO queryUserById(@PathVariable("id") Long userId){
        User user = userService.getById(userId);
        UserVO userVO = new UserVO();
        BeanUtil.copyProperties(user, userVO);
        return userVO;
    }

    @GetMapping
    @ApiOperation("根据ids查询批量用户")
    public List<UserVO> queryUserByIds(@RequestParam("ids") List<Long> ids){
        List<User> users = userService.listByIds(ids);
        List<UserVO> userVOS = BeanUtil.copyToList(users, UserVO.class);
        return userVOS;
    }

    @PutMapping("/{id}/deduction/{money}")
    @ApiOperation("根据id更新金额")
    public void deduction(@PathVariable("id") Long id, @PathVariable Integer money){
        userService.deduction(id, money);
    }

}

UserServiceImpl

特别注意:ServiceImpl已经注入了badeMapper(泛型指定为userMapper),所以可以直接调用mapper无需注入

package com.itheima.mp.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.service.IUserService;
import org.springframework.stereotype.Service;

/**
 * @author Mr.Lu
 * @version 1.0
 * @date 2024-08-13 20:53
 */

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Override
    public void deduction(Long id, Integer 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.扣减余额
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>()
                .eq(User::getId, id);
        baseMapper.deduction(money, warpper);
    }
}

IUserService

package com.itheima.mp.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;

/**
 * @author Mr.Lu
 * @version 1.0
 * @date 2024-08-13 20:52
 */

public interface IUserService extends IService<User> {
    void deduction(Long id, Integer money);
}

UserMapper

package com.itheima.mp.mapper;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;


@Mapper
public interface UserMapper extends BaseMapper<User> {

    @Update("update tb_user set balance = balance - #{money} ${ew.customSqlSegment}")
    void deduction(@Param("money")Integer money, @Param("ew")LambdaUpdateWrapper<User> wrapper);
}

IService中还提供了Lambda功能来简化我们的复杂查询及更新功能。我们通过两个案例来学习一下。

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

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

可以理解成一个用户的后台管理界面,管理员可以自己选择条件来筛选用户,因此上述条件不一定存在,需要做判断。

首先定义一个查询条件实体,UserQuery实体:

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

在UserController中定义一个controller方法:

@GetMapping("/list")
@ApiOperation("根据id集合查询用户")
public List<UserVO> queryUsers(UserQuery query){
    // 1.组织条件
    String username = query.getName();
    Integer status = query.getStatus();
    Integer minBalance = query.getMinBalance();
    Integer maxBalance = query.getMaxBalance();
    LambdaQueryWrapper<User> wrapper = new QueryWrapper<User>().lambda()
            .like(username != null, User::getUsername, username)
            .eq(status != null, User::getStatus, status)
            .ge(minBalance != null, User::getBalance, minBalance)
            .le(maxBalance != null, User::getBalance, maxBalance);
    // 2.查询用户
    List<User> users = userService.list(wrapper);
    // 3.处理vo
    return BeanUtil.copyToList(users, UserVO.class);
}

在组织查询条件的时候,我们加入了 username != null 这样的参数,意思就是当条件成立时才会添加这个查询条件,类似Mybatis的mapper.xml文件中的标签。

案例二:批量新增,修改项目中的application.yml文件,在jdbc的url后面添加参数&rewriteBatchedStatements=true

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

3. 扩展功能

3.1 代码生成

在这里插入图片描述
在这里插入图片描述

3.2 静态工具

有的时候Service之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个静态工具类:Db,其中的一些静态方法与IService中方法签名基本一致,也可以帮助我们实现CRUD功能:

在这里插入图片描述

1. 改造根据id查询用户的接口,查询用户的同时,查询出用户对应的所有地址改造;

    @Override
    public UserVO queryUserAndAddress(Long userId) {
        // 1. 查询用户信息
        User user = (User) Db.getById(userId, User.class);
        // 2. 判断用户是否存在
        if(user == null) return null;
        // 3. 查询用户地址
        List<Address> address = Db.lambdaQuery(Address.class).eq(Address::getUserId, userId).list();
        // 类型转换
        UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
        List<AddressVO> addressVOList = BeanUtil.copyToList(address, AddressVO.class);
        userVO.setAddresses(addressVOList);

        return userVO;
    }

2. 根据id批量查询用户的接口,查询用户的同时,查询出用户对应的所有地址;

 public List<UserVO> queryUsersAndAddresses(List<Long> ids) {
        // 1. 批量查询用户
        List<User> users = Db.listByIds(ids, User.class);
        if(CollUtil.isEmpty(users)){
            return Collections.emptyList();
        }
        users.forEach(System.out::println);

        // 2. 查询地址
        // 需要重新获取userIds,不能再直接使用ids, userIds保证都是存在的用户
        List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
        List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
        List<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);
        Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);
        if(CollUtil.isNotEmpty(addressVOList)){
            addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
        }
        

        // 3. 转换VO返回
        List<UserVO> list = new ArrayList<>(users.size());

        for(User user : users){
            UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
            userVO.setAddresses(addressMap.get(user.getId()));
            list.add(userVO);
        }

        return list;
    }

3.3 逻辑删除

对于一些比较重要的数据,我们往往会采用逻辑删除的方案,即:

  • 在表中添加一个字段标记数据是否被删除
  • 当删除数据时把标记置为true
  • 查询时过滤掉标记为true的数据

一旦采用了逻辑删除,所有的查询和删除逻辑都要跟着变化,非常麻烦。开启了逻辑删除功能以后,我们就可以像普通删除一样做CRUD,基本不用考虑代码逻辑问题。还是非常方便的。

然后给Address实体添加deleted字段

在这里插入图片描述

在application.yml中配置逻辑删除字段

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

测试

    @Test
    void testDeleteByLogic() {
        // 删除方法与以前没有区别
        addressService.removeById(59L);
    }

在这里插入图片描述

 @Test
    void testQuery() {
        List<Address> list = addressService.list();
        list.forEach(System.out::println);
    }

在这里插入图片描述

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

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

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

4. 插件功能

4.1 分页插件

在未引入分页插件的情况下,MybatisPlus是不支持分页功能的,IService和BaseMapper中的分页方法都无法正常起效。
所以,我们必须配置分页插件。

配置分页插件

在这里插入图片描述

编写一个分页查询的测试

    @Test
    void testPage(){
        int pageNo = 1, pageSize = 2;
        // 1. 分页参数
        Page<User> page = Page.of(pageNo, pageSize);
        // 1.1 设置排序条件
        page.addOrder(new OrderItem("balance", true));
        page.addOrder(new OrderItem("id", true));

        // 2. 分页查询
        Page<User> p = userService.page(page);

        // 3. 解析
        long total = p.getTotal();
        System.out.println("total: " + total);
        long pages = p.getPages();
        System.out.println("pages: " + pages);
        List<User> users = p.getRecords();
        users.forEach(System.out::println);
    }

在这里插入图片描述

4.2 通用分页实体

现在要实现一个用户分页查询的接口,接口规范如下:

在这里插入图片描述

返回值

在这里插入图片描述

需要定义3个实体

  • UserQuery:分页查询条件的实体,包含分页、排序参数、过滤条件

  • PageDTO:分页结果实体,包含总条数、总页数、当前页数据

  • UserVO:用户页面视图实体

UserQuery具体代码如下

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

其中缺少的仅仅是分页条件,而分页条件不仅仅用户分页查询需要,以后其它业务也都有分页查询的需求。因此建议将分页查询条件单独定义为一个PageQuery实体:

PageQuery是前端提交的查询参数,一般包含四个属性:

  • pageNo:页码

  • pageSize:每页数据条数

  • sortBy:排序字段

  • isAsc:是否升序

@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {
    @ApiModelProperty("页码")
    private Long pageNo;
    @ApiModelProperty("页码")
    private Long pageSize;
    @ApiModelProperty("排序字段")
    private String sortBy;
    @ApiModelProperty("是否升序")
    private Boolean isAsc;
}

然后让UserQuery继承这个实体:

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

PageDTO具体代码如下

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

UserVO具体代码如下

package com.itheima.mp.domain.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.List;

@Data
@ApiModel(description = "用户VO实体")
public class UserVO {

    @ApiModelProperty("用户id")
    private Long id;

    @ApiModelProperty("用户名")
    private String username;

    @ApiModelProperty("详细信息")
    private String info;

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

    @ApiModelProperty("账户余额")
    private Integer balance;

    @ApiModelProperty("用户使用地址")
    private List<AddressVO> addresses;
}

开发接口

在UserController中定义分页查询用户的接口

package com.itheima.mp.controller;

import cn.hutool.core.bean.BeanUtil;
import com.itheima.mp.domain.dto.PageDTO;
import com.itheima.mp.domain.dto.UserFormDTO;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.query.UserQuery;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;



@RequestMapping("/users")
@RestController
@Slf4j
@Api(tags = "用户管理接口")
public class UserController {
    @Resource
    private IUserService userService;

    @GetMapping("/page")
    @ApiOperation("分页查询")
    public PageDTO<UserVO> queryUsersPage(UserQuery query){
        return userService.queryUsersPage(query);
    }
}

在IUserService中创建queryUsersPage方法

public interface IUserService extends IService<User> {

    PageDTO<UserVO> queryUsersPage(UserQuery query);
}

在UserServiceImpl中实现该方法

package com.itheima.mp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.itheima.mp.domain.dto.PageDTO;
import com.itheima.mp.domain.po.Address;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.query.UserQuery;
import com.itheima.mp.domain.vo.AddressVO;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.service.IUserService;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Override
    public PageDTO<UserVO> queryUsersPage(UserQuery query) {
        // 1. 构造条件
        // 1.1 分页条件
        int pageNo = query.getPageNo();
        int pageSize = query.getPageSize();
        Page<User> page = Page.of(pageNo, pageSize);
        // 1.2 排序条件
        if(query.getSortBy() != null){
            page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));
        } else {
            // 默认按照更新时间排序
            page.addOrder(new OrderItem("update_time", false));
        }

        // 2. 查询
        // userService.page(page),由于是userService类自身的方法,所以不用再写userService
        // 2.1 不带筛选条件的
        // Page<User> p = page(page);

        // 2.2 带筛选条件的
        Page<User> p = lambdaQuery()
                .like(query.getName() != null, User::getUsername, query.getName())
                .eq(query.getStatus() != null, User::getStatus, query.getStatus())
                .ge(query.getMaxBalance() != null, User::getBalance, query.getMinBalance())
                .lt(query.getMinBalance() != null, User::getBalance, query.getMaxBalance()).page(page);

        // 3. 数据校验是否合法
        List<User> users = p.getRecords();
        if(users == null || users.size() <= 0){
            // 无数据,返回空结果
            return new PageDTO<>(p.getTotal(), p.getPages(), Collections.emptyList());
        }

        // 4. 有数据转换
        List<UserVO> list = BeanUtil.copyToList(users, UserVO.class);

        // 5 返回封装结果
        return new PageDTO<>(p.getTotal(), p.getPages(), list);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2043908.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

初阶数据结构排序之插入排序

排序01 插⼊排序 基本思想 直接插⼊排序是⼀种简单的插⼊排序法&#xff0c;其基本思想是&#xff1a;把待排序的记录按其关键码值的⼤⼩逐个插 ⼊到⼀个已经排好序的有序序列中&#xff0c;直到所有的记录插⼊完为⽌&#xff0c;得到⼀个新的有序序列 。 实际中我们玩扑克牌…

uniapp 日常业务 随便写写 源码

现成的组件 直接用 <template><view style"margin: 10rpx;"><view class"tea-header"><text class"tea-title">礼尚往来</text><view class"tea-view-all"><text>查看全部</text>&l…

Redis 如何实现高并发

Redis 如何实现高并发 1、架构概述2、读写分离的优势3、注意事项 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; Redis&#xff0c;作为一个高性能的键值对存储系统&#xff0c;通过其独特的设计和优化策略&#xff0c;能够有效地支持高并发…

关于TM611AWLCOR连续液位检测传感器的使用明细

1. 前言 本文只做软件协议相关的使用说明&#xff0c;对于硬件设计相关不做讨论。 本使用明细中涉及到的所有文档均来自诺泰官方技术支持并征得同意进行技术公开交流。其中涉及的代码均由我本人编写&#xff0c;仅供交流学习。 2. 数据手册 经由淘宝“青岛诺泰微电子有限公司”…

【添加与搜索单词 - 数据结构设计】python刷题记录

R4-位运算 Trie树BFS处理. class WordDictionary:def __init__(self):self.root{}def addWord(self, word: str) -> None:nodeself.rootfor c in word:if c not in node:node[c]{}nodenode[c]node["#"]{}def search(self, word: str) -> bool:word"#&quo…

MacOS上安装 Java

1.下载 oracle官网jdk下载地址 注意一下区分mac芯片版本&#xff0c;M1芯片选择Arm 64&#xff0c;Intel芯片选择x64 2.安装 傻瓜式安装&#xff0c;下载好后直接双击打开,一直下一步安装即可 3.查看安装路径 可通过以下命令查看安装路径(复制此输出路径&#xff0c;为后续…

Linux下ETCD安装、配置、命令详解

目录 1. 安装 Etcd 通过包管理器安装 从源代码编译安装 2. 配置 Etcd 3. 启动 Etcd 4. 使用 Etcd Etcd 是一个分布式的键值存储系统&#xff0c;主要用于服务发现、配置管理以及共享数据等场景。在 Linux 下安装、配置和使用 Etcd 涉及到几个步骤&#xff0c;下面我将详细…

DevExpress WPF中文教程:如何在GridControl中显示摘要?

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

NLP实验-基于预训练模型的文本分类

使用BERT及其变体实现AclImdb情感分类 前言数据集介绍【Hugging Face】使用方法和如何挑选一个自己需要的模型 基于BERT预训练模型的本文分类数据预处理载入文本标记器将数据转化为模型可以接受的格式训练模型加载模型 基于RoBerta预训练模型的文本分类基于DeBerta预训练模型的…

使用STM32定时器的PWM功能控制电机

目录 概述 1 系统框架结构 1.1 框架结构介绍 1.2 STM32 Cube配置PWM参数 2 软件实现 2.1 STM32Cube生成项目 2.2 PWM功能的User函数接口 3 测试 3.1 编写测试函数 3.2 功能测试 概述 本文主要介绍使用STM32定时器TIMER-8功能生成4路PWM&#xff0c;用于控制两路电机…

五种Slowing Changing Dimensions(SCD)方法及案例

SCD Type Description Key Features Type 1 Overwriting the existing data with new data, without keeping any history of the previous values. 直接覆盖&#xff0c;不留痕迹 - Overwrites Existing Data - No Historical Data - Simple Implementation Type…

Composerize神器:自动化转换Docker运行命令至Compose配置,简化容器部署流程

Composerize神器&#xff1a;自动化转换Docker运行命令至Compose配置&#xff0c;简化容器部署流程 在现代的微服务架构中&#xff0c;Docker Compose 是管理多容器应用的重要工具&#xff0c;它允许我们通过一个简单的 docker-compose.yml 文件来定义和运行多个关联的容器。然…

重发布实验

一、实验拓扑图 二、实验需求 1.如图搭建网络拓扑&#xff0c;所有路由器各自创建一个环回接口&#xff0c;合理规划IP地址 2.R1-R2-R3-R4-R6之间使用OSPF协议&#xff0c;R4-R5-R6之间使用RIP协议 3.R1环回重发布方式引入OSPF网络 4.R4/R6上进行双点双向重发布 5.分析网络…

CSP-CCF 202012-1 期末预测之安全指数

一、问题描述 二、解答 #include<iostream> using namespace std; int main() {int n;cin >> n;int w[100001] { 0 };int score[100001] { 0 };for (int i 1; i < n; i){cin >> w[i] >> score[i];}int y 0;for (int i 1; i < n; i){y y …

Java——反射(2/4):获取构造器对象并使用(获取类的构造器、并对其进行操作,获取类构造器的作用,代码实例)

目录 获取类的构造器 获取类的构造器、并对其进行操作 代码实例一 代码实例二 获取类构造器的作用 代码示例三 获取类的构造器 获取类的构造器、并对其进行操作 Class提供了从类中获取构造器的方法。 方法说明Constructor<?>[] getConstructors()获取全部构造器…

激光测距传感器

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 前言一、产品原理&#xff1a;二、产品介绍&#xff1a;三、应用特点四、应用案例&#xff1a;1.冶金钢铁板卷材开卷工…

深入理解JavaScript性能优化:从基础到高级

引言 在当今快速发展的Web世界中,性能已经成为衡量应用质量的关键指标。随着Web应用复杂度的不断提升,JavaScript作为前端开发的核心语言,其性能优化变得尤为重要。本文旨在全面深入地探讨JavaScript性能优化的各个方面,从基础概念到高级技巧,帮助开发者构建高效、流畅的Web应用…

Android Studio本地加速安装gradle

Android Studio本地加速安装gradle 镜像下载依赖本地JAVA-JDK配置阿里云镜像配置环境变量验证gradle项目文件的介绍项目配置gradle项目Gradle-Wrapper加速配置&#xff0c;防止下载失败Gradle的常用命令 镜像下载 腾讯软件镜像源&#xff1a;https://mirrors.cloud.tencent.co…

50ETF期权移仓是什么?50ETF期权移仓要注意什么?

今天带你了解50ETF期权移仓是什么&#xff1f;50ETF期权移仓要注意什么&#xff1f;当前火热的期权交易市场&#xff0c;“移仓”同样是一门非常重要的技术。上证50ETF期权投资的过程中&#xff0c;我们可以进行一定的移仓操作的&#xff0c;如果移仓操作得好&#xff0c;可以很…

CSP-CCF 202104-1 灰度直方图

一、问题描述 二、解答 思路&#xff1a;用一个二维数组和一个一维数组、以及三个嵌套的for循环即可 代码&#xff1a; #include<iostream> using namespace std; int A[500][500] { 0 }; int main() {int n, m, L;cin >> n >> m >> L;int h[256] …