您好,欢迎来到伴沃教育。
搜索
您的当前位置:首页MybatisPlus小白入门学习干货!!

MybatisPlus小白入门学习干货!!

来源:伴沃教育

前言
文章为MybatisPlus笔记,实战案例比较多,小白学习建议收藏练习~
24.10.20最新MybatisPlus已更完

Mybatis

非常流行的持久层框架,主要用来做数据库的增删改查。

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原理

常用注解

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

自定义SQL

我们可以利用 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>

Servive接口

上面介绍了使用 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;
}

逻辑删除

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

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

一旦采用了逻辑删除,所有的查询和删除逻辑都要跟着变化,非常麻烦。为了解决这个问题,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;
    }
}

JSON处理器

基本使用方法与枚举处理器类似:

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

本站由北京市万商天勤律师事务所王兴未律师提供法律服务