前言
文章为MybatisPlus笔记,实战案例比较多,小白学习建议收藏练习~
24.10.20最新MybatisPlus已更完
非常流行的持久层框架,主要用来做数据库的增删改查。
对于 Mybatis 框架的增强和升级,但并非替代 Mybatis 。是用来简化或省略单表的CRUD开发工作。
下面,我们来做一个实战案例:
使用步骤:
1、引入 MybatisPlus 的起步依赖
MybatisPlus 官方提供了 starter ,其中继承了 Mybatis 和 MybatisPlus 的所有功能,并实现了自动装配效果。
依赖如下:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
由于这个 starter 包含对 mybatis 的自动装配,因此完全可以替换掉 Mybatis 的 starter。
2、定义 Mapper
为了简化单表CRUD,MybatisPlus 提供了一个基础的 BaseMapper 接口,其中已经实现了单表的 CRUD:
自定义的 Mapper 继承 MybatisPlus 提供的 BaseMapper 接口。
如下图:
这里需要注意的是,BaseMapper 后指定的泛型为这个 Mapper 的实体类,这样才知道增删改查的是哪个实体。
如图是使用 Mybatis 手写的 CRUD:
内容多且复杂,现在我们已经做好了 MybatisPlus 的准备操作,让我们使用 MybatisPlus 来简化代码编写吧。
首先删除原本编写好的 Mapper:
原本编写好的 Mapper 被删除后,测试类自然也会报错:
咱们用 MybatisPlus 来重写方法,打个 “.” ,就发现,idea 已经给我们提示出来了 BaseMapper 接口为我们提供好的方法,我们选择并使用即可。
重写后的测试类:
再来测试下功能:执行新增
可以发现是执行成功了,数据库表中也有我们刚才插入的新数据。剩下的删改查这里就不做演示了,同样也是可以正常使用的。
那么大家肯定很好奇为什么 MybatisPlus 可以这么方便呢,接下来来解释一下 MybatisPlus 的原理。
MybatisPlus 通过扫描实体类,并基于反射获取实体类信息作为数据库表信息。这也就是上面在 Mapper 继承 BaseMapper 时强调的需要指定实体类泛型。通过反射,拿到字节码,就获得了实体类信息,然后就可以把实体类信息按照约定作为数据库表信息了。这里提到的约定如下:
•类名驼峰转下划线作为表名
•名为id的字段作为主键
•变量名驼峰转下划线作为表的字段名
符合约定的实体类就不需要我们自己配置,但如果不符合约定,就需要我们自定义配置了。在 MybatisPlus 中给我们提供了很多自定义配置的注解,常见的三个注解分别为:
•@TableName:用来指定表名
•@TableId:用来指定表中的主键字段信息
•@TableField:用来指定表中的普通字段信息
具体使用方式如下(根据下方数据库表信息,添加合适的注解):
其中涉及到的注意事项:
IdType枚举:
•AUTO:数据库自增长
•INPUT:通过set方法自行输入
•ASSIGN_ID:分配 ID,接口IdentifierGenerator的方法nextId来生成id,默认实现类为DefaultIdentifierGenerator雪花算法
使用@TableField的常见场景:
•成员变量名与数据库字段名不一致
•成员变量名以is开头,且是布尔值
•成员变量名与数据库关键字冲突
•成员变量不是数据库字段
在之前的案例中,我们配置好了 starter 后 MybatisPlus 就可以自动装配,直接就可以使用,同时, MybatisPlus 也支持很多常见配置,详见官方文档:
https://b11et3un53m.feishu.cn/wiki/PsyawI04ei2FQykqfcPcmd7Dnsc#share-CgzDdOb05oocENxIFJLcpY2znic
刚才的案例中都是以id为条件的简单CRUD,一些复杂条件的SQL语句就要用到一些更高级的功能了。
MybatisPlus 支持各种复杂的 where 条件,可以满足日常开发的所有需求。
参数中的 Wrapper 就是条件构造的抽象类,其下有很多默认实现,继承关系如图:
接下来我们来写几个案例:如何利用 Wrapper 实现复杂查询,无论是修改、删除、查询,都可以使用 QueryWrapper 来构建查询条件。
图中展示的是正常的 SQL 语句,接下来利用 MybatisPlus 的条件构造器来构造这些语句。
需求1:
@Test
void testQueryMapper(){
//1.构建查询条件
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);
}
需求2:
@Test
void testUpdateByQueryWrapper(){
//1.要更新的数据
User user = new User();
user.setBalance(2000);
//2.更新的条件
QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "jack");
//3.执行更新
userMapper.update(user, wrapper);
}
基于 BaseMapper 中的 update 方法更新时只能直接赋值,对于一些复杂的需求就难以实现。这个时候就要利用 UpdateWrapper 中的 setSql 功能了。
@Test
void testUpdateWrapper(){
List<Long> ids = List.of(1L, 2L, 4L);
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200")
.in("id", ids);
userMapper.update(null, wrapper);
}
我们可以利用 MybatisPlus 的 wrapper 来构建复杂的 where 条件,然后自己定义 SQL 语句中剩下的部分。在之前的案例中,我们都是直接通过 MybatisPlus 来生成,相当于全自动,现在只有 where 条件由 MybatisPlus 生成,相当于半自动。为什么要这么做呢,接下来我们来看两个实际案例:
这个案例很眼熟吧,我们上面使用 MybatisPlus 写过:
这样写将原本复杂的 SQL 语句简化了,按理说是很方便啦,实际不是的,我们现在写的这些逻辑其实是业务逻辑,将来是在 Service 层去定义,相当于我们把 SQL 语句写在了实际业务代码当中了,这在很多企业的开发规范中是不允许的,此时就需要自定义 SQL 了。
UserMapperTest:
@Test
void testCustomSqlUpdate(){
//1.更新条件
List<Long> ids = List.of(1L, 2L, 4L);
int amount = 200;
//2.定义条件
QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);
//3.调用自定义的SQL方法
userMapper.updateBalanceByIds(wrapper, amount);
}
UserMapper:
void updateBalanceByIds(@Param(Constants.WRAPPER) QueryWrapper<User> wrapper, @Param("amount") int amount);
UserMapper.xml:
<update id="updateBalanceByIds">
update user set balance = balance - #{amount} ${ew.customSqlSegment}
</update>
上面介绍了使用 MybatisPlus 可以省去写 Mapper 增删改查的方法。MybatisPlus 同时也很贴心的为我们准备了 Service 接口,只要继承了它,一些基础的增删改查的 Service 代码,也不用写了。通用接口为 IService ,默认实现为 ServiceImpl ,其中封装的方法可以分为以下几类:
save
:新增remove
:删除update
:更新get
:查询单个结果list
:查询集合结果count
:计数page
:分页查询具体流程如下图:
由于 Service 中经常需要定义与业务有关的自定义方法,因此我们不能直接使用 IService ,而是自定义 Service 接口,然后继承 IService 以拓展方法。同时,让自定义的 Service 实现类继承 ServiceImpl ,这样就不用自己实现 IService 中的接口了。
下面我们来写一个案例:
IUserService:
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:
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.mapper.UserMapper;
import com.itheima.mp.service.IUserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}
IUserServiceTest:
package com.itheima.mp.service;
import com.itheima.mp.domain.po.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDateTime;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class IUserServiceTest {
@Autowired
private IUserService userService;
@Test
void testSaveUser(){
User user = new User();
//user.setId(5L);
user.setUsername("LiLei");
user.setPassword("123");
user.setPhone("1860011");
user.setBalance(200);
user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
userService.save(user);
}
}
介绍完了 Mapper 接口和 Service 接口,大家会发现,二者中有很多的方法都是重复的,从功能上是一致的,那么在实际业务开发中,到底该使用哪个接口中提供的方法呢,接下来我们来看一个案例:
首先导入相关依赖:
pom.xml:
<!--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>
application.yaml:
knife4j:
enable: true
openapi:
title: 用户管理接口文档
description: "用户管理接口文档"
email: zhanghuyi@itcast.cn
concat: 虎哥
url: https://www.itcast.cn
version: v1.0.0
group:
default:
group-name: default
api-rule: package
api-rule-resources:
- com.itheima.mp.controller
创建 DTO、VO:
UserFormDTO:
package com.itheima.mp.domain.dto;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(description = "用户表单实体")
public class UserFormDTO {
@ApiModelProperty("id")
private Long id;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("密码")
private String password;
@ApiModelProperty("注册手机号")
private String phone;
@ApiModelProperty("详细信息,JSON风格")
private String info;
@ApiModelProperty("账户余额")
private Integer balance;
}
UserVO:
package com.itheima.mp.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@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;
}
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 io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "用户管理接口")
@RequestMapping("/users")
@RestController
@RequiredArgsConstructor
public class Usercontroller {
private final IUserService userService;
@ApiOperation("新增用户接口")
@PostMapping
public void saveUser(@RequestBody UserFormDTO userFormDTO){
//1.把DTO拷贝到PO
User user = BeanUtil.copyProperties(userFormDTO, User.class);
//2.新增
userService.save(user);
}
@ApiOperation("删除用户接口")
@DeleteMapping("/{id}")
public void deleteUserById(@ApiParam("用户id") @PathVariable("id") Long id){
userService.removeById(id);
}
@ApiOperation("根据id查询用户接口")
@GetMapping("/{id}")
public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long id){
//1.查询用户PO
User user = userService.getById(id);
//2.把PO拷贝到VO
return BeanUtil.copyProperties(user, UserVO.class);
}
@ApiOperation("根据id批量查询用户接口")
@GetMapping
public List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids){
//1.查询用户PO
List<User> users = userService.listByIds(ids);
//2.把PO拷贝到VO
return BeanUtil.copyToList(users, UserVO.class);
}
@ApiOperation("扣减用户余额接口")
@PutMapping("/{id}/deduction/{money}")
public void deductBalance(@ApiParam("用户id") @PathVariable("id") Long id,
@ApiParam("扣减的金额") @PathVariable("money") Integer money){
userService.deductBalance(id, money);
}
}
IUserService:
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> {
void deductBalance(Long id, Integer money);
}
UserServiceImpl:
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.mapper.UserMapper;
import com.itheima.mp.service.IUserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Override
public void deductBalance(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.扣减余额
baseMapper.deductBalance(id, money);
}
}
UserMapper:
package com.itheima.mp.mapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import java.util.List;
public interface UserMapper extends BaseMapper<User> {
void updateBalanceByIds(@Param(Constants.WRAPPER) QueryWrapper<User> wrapper, @Param("amount") int amount);
@Update("update user set balance = balance - #{money} where id = #{id}")
void deductBalance(@Param("id") Long id, @Param("money") Integer money);
}
通过这个案例可以发现,只有当业务逻辑相对复杂的时候,需要自己写一些业务,而 MybatisPlus 只提供一些基础的增删改查,这个时候就需要去自定义 service 方法了,并且在里面编写相应的业务代码,Mapper 同理,当出现 BaseMapper 提供的方法不足以满足我们的需求时,就需要我们去自定义了。
接下来,我们来看一下 IService 中提供的跟 Lambda 有关的几个方法,我们通过一个案例来看,案例需求如下:
UserController:
@ApiOperation("根据复杂条件查询用户接口")
@GetMapping("/list")
public List<UserVO> queryUsers(UserQuery userQuery){
//1.查询用户PO
List<User> users = userService.queryUsers(userQuery.getName(), userQuery.getStatus(), userQuery.getMinBalance(), userQuery.getMaxBalance());
//2.把PO拷贝到VO
return BeanUtil.copyToList(users, UserVO.class);
}
IUserService:
List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance);
UserServiceImpl:
@Override
public List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {
// List<User> users = lambdaQuery()
// .like(name != null, User::getUsername, name)
// .eq(status != null, User::getStatus, status)
// .ge(minBalance != null, User::getBalance, minBalance)
// .le(maxBalance != null, User::getBalance, maxBalance)
// .list();
// return users;
return lambdaQuery()
.like(name != null, User::getUsername, name)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance)
.list();
}
再看一个案例,需求如下:
该案例之前做过,所以进行改造即可。
UserServiceImpl:
@Override
@Transactional
public void deductBalance(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.扣减余额
int remainBalance = user.getBalance() - money;
lambdaUpdate()
.set(User::getBalance, remainBalance)
.set(remainBalance == 0, User::getStatus, 2)
.eq(User::getId, id)
.eq(User::getBalance, user.getBalance())//乐观锁
.update();
}
MybatisPlus 的核心功能已经讲完了,接下来我们来讲讲 MybatisPlus 的扩展功能。
当我们使用 MybatisPlus 进行开发的时候,有一系列操作是必须要做的,如下图所示:
这些固定的代码,每次处理不同的业务时都需要我们去敲是不是很费时间,这时就可以用到我们 MybatisPlus 的代码生成功能。
这里给大家推荐一款插件,用它可以直接根据数据库表生成对应的基础代码,具体的操作流程如下:
这样就可以生成好基础代码啦~
首先,我们先来看一下静态工具为我们提供了哪些方法:
是不是很眼熟,这些功能和 IService 接口中的功能很相似,二者的区别就在于 Iservice 接口需要指定泛型,告诉它实体类的信息,而静态工具不需要,它需要在每次使用时携带参数。那么既然两者的功能如此类似,为什么还需要多这么一个工具呢,接下来我们来看一个实际案例:
从这个案例可以看到,当我们想要实现业务的时候,有些业务在 UserService 中,但是需要去查 AddressService 那么就需要注入,而有些业务呢,在 AddressService 中,需要去查 UserService 这时候呢,就又需要去注入 UserService ,这样两个 Service 需要相互注入,就形成了循环依赖的情况。这个时候就需要用到静态查询工具,就可以避免这样的情况。
首先在 UserVO 中加一个属性:
UserController:
@ApiOperation("根据id查询用户接口")
@GetMapping("/{id}")
public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long id){
return userService.queryUserAndAddressById(id);
}
UserService:
UserVO queryUserAndAddressById(Long id);
UserServiceImpl:
@Override
public UserVO queryUserAndAddressById(Long id) {
//1.查询用户
User user = getById(id);
if (user == null || user.getStatus() == 2){
throw new RuntimeException("用户状态异常!");
}
//2.查询地址
List<Address> addresses = Db.lambdaQuery(Address.class).eq(Address::getUserId, id).list();
//3.封装VO
//3.1UserPO转VO
UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
//3.2转地址VO
if(CollUtil.isNotEmpty(addresses)){
userVO.setAddress(BeanUtil.copyToList(addresses, AddressVO.class));
}
return userVO;
}
UserController:
@ApiOperation("根据id批量查询用户接口")
@GetMapping
public List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids){
return userService.queryUsersAndAddressByIds(ids);
}
UserService:
List<UserVO> queryUsersAndAddressByIds(List<Long> ids);
UserServiceImpl:
@Override
public List<UserVO> queryUsersAndAddressByIds(List<Long> ids) {
//1.查询用户
List<User> users = listByIds(ids);
if (CollUtil.isEmpty(users)){
return Collections.emptyList();
}
//2.查询地址
//2.1获取用户id集合
List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
//2.2根据用户id查询地址
List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
//查询Address表 where条件 返回集合
//2.3转地址VO
List<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);
//2.4对用户地址集合进行分组处理,相同用户的放入一个集合(组)中
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) {
//3.1UserPO转VO
UserVO vo = BeanUtil.copyProperties(users, UserVO.class);
list.add(vo);
//3.2转地址VO
vo.setAddress(addressMap.get(user.getId()));
}
return list;
}
对于一些比较重要的数据,我们往往会采用逻辑删除的方案,即:
一旦采用了逻辑删除,所有的查询和删除逻辑都要跟着变化,非常麻烦。为了解决这个问题,MybatisPlus 添加了对逻辑删除的支持。
以一个实际案例举例:
先定义一个枚举类UserStatus:
要让 MybatisPlus 处理枚举与数据库类型自动转换,我们必须告诉 MybatisPlus ,枚举中的哪个字段的值作为数据库值。 MybatisPlus 提供了 @EnumValue 注解来标记枚举属性:
package com.itheima.mp.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
@Getter
public enum UserStatus {
NORMAL(1,"正常"),
FROZEN(2,"冻结"),
;
@EnumValue
private final int value;
private final String desc;
UserStatus(int value, String desc) {
this.value = value;
this.desc = desc;
}
}
将实体类的 status 由 Integer 改为我们定义的枚举类:
/**
* 使用状态(1正常 2冻结)
*/
private UserStatus status;
然后配置枚举处理器,在 application.yaml 文件中添加配置:
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
最后再将实现类中原先引用的 Integer 改为枚举类就好:
当然,我们光是在 PO 中进行了改动是不够的,在代码中,我们将 PO 的属性拷贝给了 VO,所以 VO 中也需要更改,不然属性是拷贝不进去的,因此:
UserVO:
@ApiModelProperty("使用状态(1正常 2冻结)")
private UserStatus status;
枚举在做返回的时候,默认会以枚举项的名字进行返回,就是我们定义的 NORMAL、FROZEN,如果想让它以 value 或者 desc 中文返回,就需要告诉 springMVC,我们的数据往前端返回是由 springMVC 处理的,其底层在处理 JSON 时用到了 JacksonJSON,它提供了一个注解 @JsonValue,将 @JsonValue 加到 value 上,返回的就是 value ,加到 desc 上,返回的就是desc。为了更加直观,我们选择返回 desc:
UserStatus:
package com.itheima.mp.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
@Getter
public enum UserStatus {
NORMAL(1,"正常"),
FROZEN(2,"冻结"),
;
@EnumValue
private final int value;
@JsonValue
private final String desc;
UserStatus(int value, String desc) {
this.value = value;
this.desc = desc;
}
}
基本使用方法与枚举处理器类似:
UserInfo:
package com.itheima.mp.domain.po;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor//无参构造
@AllArgsConstructor(staticName = "of")//有参构造
public class UserInfo {
private int age;
private String intro;
private String gender;
}
User:
@Data
@TableName(value = "user", autoResultMap = true)//自动结果集映射
public class User {
/**
* 详细信息
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private UserInfo info;
}
UserVO:
package com.itheima.mp.domain.vo;
import com.itheima.mp.domain.po.UserInfo;
import com.itheima.mp.enums.UserStatus;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(description = "用户VO实体")
public class UserVO {
@ApiModelProperty("详细信息")
private UserInfo info;
}
接下来我们来学 MybatisPlus 的插件功能,MybatisPlus 提供了许多插件供我们使用:
我们主要学习分页插件
在未引入分页插件的情况下,MybatisPlus 是不支持分页功能的, IService 和 BaseMapper 中的分页方法都无法正常起效。 所以,我们必须配置分页插件。
在项目中新建一个配置类 MyBatisConfig:
package com.itheima.mp.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//1.创建分页插件
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setMaxLimit(1000L);
//2.添加分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
}
IUserServiceTest:
@Test
void testPageQuery() {
int pageNumber = 1, pageSize = 2;
//1.准备分页条件
//1.1分页条件
Page<User> page = Page.of(pageNumber, pageSize);
//1.2排序条件
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);
}
这与真实开发还有一定的差距,接下来我们来看一个案例:
UserController:
@ApiOperation("根据复杂条件分页查询用户接口")
@GetMapping("/page")
public PageDTO<UserVO> queryUsersPage(UserQuery userQuery){
return userService.queryUsersPage(userQuery);
}
IUserService:
PageDTO<UserVO> queryUsersPage(UserQuery userQuery);
UserServiceImpl:
@Override
public PageDTO<UserVO> queryUsersPage(UserQuery userQuery) {
String name = userQuery.getName();
Integer status = userQuery.getStatus();
//1.构造分页条件
//1.1分页条件
Page<User> page = Page.of(userQuery.getPageNo(), userQuery.getPageSize());
//1.2排序条件
if (StrUtil.isNotBlank(userQuery.getSortBy())){
page.addOrder(new OrderItem(userQuery.getSortBy(), userQuery.getIsAsc()));
}else {
page.addOrder(new OrderItem("update_time",false));
}
//2.分页查询
Page<User> p = lambdaQuery()
.like(name != null, User::getUsername, name)
.eq(status != null, User::getStatus, status)
.page(page);
//3.封装VO结果
PageDTO<UserVO> dto = new PageDTO<>();
//3.1总条数
dto.setTotal(p.getTotal());
//3.1总页数
dto.setPages(p.getPages());
//3.1集合
List<User> records = p.getRecords();
if (CollUtil.isEmpty(records)){
dto.setList(Collections.emptyList());
return dto;
}
//3.4拷贝user的VO
List<UserVO> userVOS = BeanUtil.copyToList(records, UserVO.class);
dto.setList(userVOS);
//4.返回
return dto;
}
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- bangwoyixia.com 版权所有 湘ICP备2023022004号-2
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务