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中比较常用的几个注解如下:
- @TableName: 用来指定表明
- @TableId: 用来指定表中的主键字段信息
- @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);
}
}