当前位置: 首页 > news >正文

MybatisPlus使用指南

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 {@ResourceUserMapper userMapper;@Testvoid testQueryWrapper(){// 1.构建查询条件 where name like "%o%" AND balance >= 1000QueryWrapper<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 {@ResourceUserMapper userMapper;@Testvoid 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 {@ResourceUserMapper userMapper;@Testvoid 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 {@ResourceUserMapper userMapper;@Testvoid testLambdaQueryWrapper(){// 1.构建查询条件 where name like "%o%" AND balance >= 1000LambdaQueryWrapper<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);}@Testvoid 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语句:

 @Testvoid 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: trueopenapi:title: 用户管理接口文档description: "用户管理接口文档"email: xxxxxconcat: xxxxurl: xxxxversion: v1.0.0group:default:group-name: defaultapi-rule: packageapi-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 {@Resourceprivate 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 {@Overridepublic 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.处理voreturn 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查询用户的接口,查询用户的同时,查询出用户对应的所有地址改造;

    @Overridepublic 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)

测试

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

在这里插入图片描述

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

在这里插入图片描述

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

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

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

4. 插件功能

4.1 分页插件

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

配置分页插件

在这里插入图片描述

编写一个分页查询的测试

    @Testvoid 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 {@Resourceprivate 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 {@Overridepublic 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);}
}

相关文章:

MybatisPlus使用指南

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

5. MongoDB 集合创建、更新、删除

1. 创建集合 1.1 语法 db.createCollection(name, options) 参数说明&#xff1a; name: 要创建的集合名称。options: 可选参数, 指定有关内存大小及索引的选项。 options 可以是如下参数&#xff1a; 参数名类型描述示例值capped布尔值是否创建一个固定大小的集合。truesize…...

PHP中如何将变量从函数传递给acf_add_filter

在PHP开发中&#xff0c;我们有时需要将变量从函数传递给acf的add_filter钩子。这样做可以让我们在acf字段加载时&#xff0c;对字段值进行动态修改。下面&#xff0c;我将详细介绍如何实现这一功能。 在acf中&#xff0c;我们使用add_filter来添加钩子&#xff0c;对字段的加…...

KNN算法的使用

目录 一、KNN 算法简介 二、KNN算法的使用 1.读取数据 2.处理数据 三、训练模型 1.导入KNN模块 2.训练模型 3.出厂前测试 四、进行测试 1.处理数据 2.进行测试 总结 一、KNN 算法简介 KNN 是一种基于实例的学习算法。它通过比较样本之间的距离来进行预测。算法的核心…...

java文件上传

导入jar包&#xff0c;或者maven <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>…...

MySQL 数据库经验总结

一、数据库操作 1. 创建数据库 CREATE DATABASE database_name;例如&#xff0c;创建一个名为 my_database 的数据库&#xff1a; CREATE DATABASE my_database;2. 选择数据库 USE database_name;要使用刚才创建的 my_database 数据库&#xff1a; USE my_database;3. 删除…...

Python环境安装及PIP安装(Mac OS版)

官网 https://www.python.org/downloads/ 安装python python-3.12.1-macos11.pkg下载后&#xff0c;安装一直下一步即可 验证是否安装成功&#xff0c;执行python3命令和pip3命令 配置环境变量 获取python3安装位置并配置在.bash_profile #查看python路径 which python3#…...

2024自动驾驶(多模态)大模型综述:从DriveGPT4、DriveMLM到DriveLM、DriveVLM

前言 由于今年以来&#xff0c;一直在不断深挖具身智能机器人相关&#xff0c;而自动驾驶其实和机器人有着无比密切的联系&#xff0c;甚至可以认为&#xff0c;汽车就是一个带着4个轮子的机器人 加之个人认为&#xff0c;目前大模型落地潜力最大的两个方向&#xff0c;一个是…...

晨控CK-GW08-EC与汇川AC801系列PLC的EtherCAT通讯连接说明手册

晨控CK-GW08-EC与汇川AC801系列PLC的EtherCAT通讯连接说明手册 晨控CK-GW08-EC是一款支持标准工业通讯协议EtherCAT的网关控制器,方便用户集成到PLC等控制系统中。系统还集成了8路读写接口&#xff0c;用户可通过通信接口使用EtherCAT协议对8路读写接口所连接的读卡器进行相对…...

向上or向下调整建堆 的时间复杂度的本质区别的讲解

知识点&#xff1a;&#xff08;N代表节点数&#xff0c;h代表高度&#xff09; 1&#xff1a;高度为h的满二叉树节点个数N为 2^&#xff08;h&#xff09;-1 即N 2^&#xff08;h&#xff09;-1 2&#xff1a;所以h log&#xff08;N1&#xff09; 一&#xff1a;向上…...

阿一网络安全实战演练之利用 REST URL 中的服务器端参数污染

所需知识 要解决这个实验室问题&#xff0c;您需要了解以下内容&#xff1a; 如何确定用户输入是否包含在服务器端的 URL 路径或查询字符串中。如何使用路径遍历序列尝试更改服务器端请求。如何查找 API 文档。 这些内容在我们的 API 测试学院主题中有涵盖。 进入实验室 研…...

[游戏开发] LuaTable转string存读二进制文件

UE5和Unity通用此方案&#xff0c;只不过读写文件的接口略有不同&#xff0c;lua代码的处理是相同的。 下面两个方法是 LuaTable和字符串互相转换的代码 function XUtils.luaTableToString(tab, sp)sp sp or ""local s ""for k,v in pairs(tab) doif t…...

光伏业务管理系统的一些妙用功能

现在信息化流程化基本上每个行业都必须要有的了&#xff0c;光伏业务管理系统软件是一种专门用于光伏产业运营和管理的综合性系统&#xff0c;它结合了信息技术、数据分析、项目管理、客户管理等多个领域的知识&#xff0c;为光伏企业提供了一个全面、高效、智能的管理平台&…...

Java面试八股之请简述消息队列的发布订阅模式

请简述消息队列的发布订阅模式 发布订阅&#xff08;Publish-Subscribe&#xff0c;简称 Pub/Sub&#xff09;模型是一种消息传递模式&#xff0c;它在组件之间提供了高度的解耦和灵活性。这种模式广泛应用于分布式系统、事件驱动架构以及消息队列系统中。下面是发布订阅模型的…...

七、2 ADC数模转换器有关函数介绍(Keil5)

函数介绍 &#xff08;1&#xff09;ADCCLK的配置函数&#xff08;在rcc.h中&#xff09; &#xff08;2&#xff09;ADC的库函数&#xff08;在adc.h中&#xff09;...

了解载波侦听多路访问CSMA(上)

1.CSMA的思想 CSMA的全称是Carrier Sense Multiple Access&#xff0c;在笔者的理解中&#xff0c;其更趋向于一种理论研究的随机接入协议&#xff0c;或者说&#xff0c;基于其思想诞生了比如CSMA/CD与CSMA/CA这样的具体协议。CSMA可以分成以下三种&#xff1a; 1-persistent…...

开启教育新征程:“集师” 知识付费平台搭建

在教育培训行业竞争日益激烈的今天&#xff0c;如何脱颖而出&#xff0c;实现知识的最大价值&#xff1f;答案就在 “集师” 知识付费平台搭建&#xff01; “集师” 为您打造专属的知识付费平台&#xff0c;提供一站式解决方案。无论您是专注于学科教育、艺术培训还是职业技能…...

Vue3 + Electron 创建新的子窗口 且子窗口唯一

main.js const { app, BrowserWindow, ipcMain } require(electron) ...ipcMain.on(window-create, () > {createChildWindow() })let childWindow nullconst createChildWindow () > {// 如果窗口存在 先销毁if (childWindow) {childWindow.destroy()childWindow n…...

海康VisionMaster使用学习笔记2-相机取图及参数设置

相机取图及参数设置 1. 关联相机-相机管理界面 除了以上两类外,第三方相机都可以通过全局相机进行连接 2. 相机参数设置 相机连接 跨网段IP,枚举 图像缓存数量 实时取流,断线重连 只有支持组播的相机才可以实时取流 触发设置 触发源 LINE0 可以保护电路 LINE2 可配置输入输出…...

【网络】【Linux】Linux内核中连接的组织形式与全连接队列

Linux内核中连接的组织形式与全连接队列 文章目录 1.前言2.Linux内核中连接的组织形式2.1套接字和文件描述符2.2创建连接 & 获取连接 3.全连接队列3.1为什么有全连接队列&#xff1f;3.2全连接队列的长度 1.前言 TCP是面向连接的&#xff0c;TCP的各种可靠性机制实际都不…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist

现象&#xff1a; android studio报错&#xff1a; [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决&#xff1a; 不要动CMakeLists.…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...