Mybatis-Plus超级实用的多种功能用法

一、Mybatis-Plus介绍

1.1、什么是 MyBatis-Plus

MyBatis-Plus 是一款基于 MyBatis 的增强型 ORM 框架,由国内团队(苞米豆)开发维护,遵循「只做增强,不做改变」的核心设计理念 ------ 它完全兼容 MyBatis 的原有功能,同时在其基础上封装了大量常用操作,极大简化了 MyBatis 的开发流程,减少了重复的 CRUD 代码编写工作。

简单来说:MyBatis 是「半自动化」ORM 框架(需要手动写 SQL),而 MyBatis-Plus 在此基础上,通过内置的通用 Mapper/Service,让开发者无需编写基础 SQL 就能完成大部分数据库操作,同时保留了 MyBatis 灵活定制 SQL 的能力。

1.2、核心定位

  • 增强:对 MyBatis 无侵入式增强,原有 MyBatis 代码无需修改即可集成。
  • 高效:大幅减少 CRUD 代码、XML 配置的编写量。
  • 灵活:支持自定义 SQL、多数据源、插件扩展等高级特性。

1.3、优势

  1. 无侵入性:完全基于 MyBatis 扩展,不修改 MyBatis 核心源码,兼容所有 MyBatis 原生功能。
  2. 强大的 CRUD 封装:内置通用 Mapper/Service,单表操作无需编写任何 SQL 即可完成增删改查。
  3. 支持 Lambda 表达式:通过 Lambda 语法构建查询条件,避免硬编码字段名,减少拼写错误。
  4. 自动分页:内置分页插件,无需手动处理分页逻辑,支持多种数据库(MySQL、Oracle、PostgreSQL 等)。
  5. 主键生成策略:内置多种主键生成策略(雪花算法、自增、UUID 等),可灵活配置。
  6. 逻辑删除 :无需手动写删除逻辑,通过标记字段(如 deleted)实现逻辑删除,保留数据。
  7. 乐观锁:内置乐观锁插件,轻松解决并发更新问题。
  8. 代码生成器:支持快速生成 Entity、Mapper、Service、Controller 等全套代码,提升开发效率。

二、Mybatis-Plus快速入门

2.1、引入依赖配置

XML 复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

2.2、xml配置

XML 复制代码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

# MyBatis-Plus 配置
mybatis-plus:
  # 实体类别名包路径
  type-aliases-package: com.example.mpdemo.entity
  # XML映射文件路径
  mapper-locations: classpath:mapper/**/*.xml
  # 配置日志(方便调试SQL)
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 全局配置
  global-config:
    db-config:
      # 主键生成策略:雪花算法(适用于分布式)
      id-type: ASSIGN_ID
      # 逻辑删除字段名
      logic-delete-field: deleted
      # 逻辑删除-未删除值
      logic-not-delete-value: 0
      # 逻辑删除-已删除值
      logic-delete-value: 1

2.3、实体类

java 复制代码
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;

@Data // Lombok注解,简化get/set
@TableName("user") // 指定数据库表名(默认类名驼峰转下划线)
public class User {
    // 主键,使用雪花算法生成
    @TableId(type = IdType.AUTO)
    private Long id;
    
    // 普通字段(默认字段名驼峰转下划线,如userName -> user_name)
    private String userName;
    
    private Integer age;
    
    private String email;
    
    // 逻辑删除字段(对应全局配置的deleted)
    @TableLogic
    private Integer deleted;
    
    // 自动填充创建时间
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    // 自动填充更新时间
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

2.4、Mapper接口

java 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mpdemo.entity.User;
import org.apache.ibatis.annotations.Mapper;

// 继承BaseMapper即可获得所有CRUD方法,无需编写任何SQL
@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 如需自定义SQL,可在此添加方法并在XML中实现
}

2.5、测试

java 复制代码
import com.example.mpdemo.entity.User;
import com.example.mpdemo.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;

@SpringBootTest
public class MpDemoTest {
    @Autowired
    private UserService userService;

    // 新增
    @Test
    public void testAdd() {
        User user = new User();
        user.setUserName("张三");
        user.setAge(20);
        user.setEmail("zhangsan@example.com");
        userService.save(user);
    }

    // 查询所有
    @Test
    public void testList() {
        List<User> userList = userService.list();
        userList.forEach(System.out::println);
    }

    // 条件查询 + 分页
    @Test
    public void testPage() {
        // 构建分页条件:第1页,每页10条
        Page<User> page = new Page<>(1, 10);
        // 构建查询条件:年龄大于18
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .gt(User::getAge, 18);
        // 分页查询
        Page<User> userPage = userService.page(page, wrapper);
        // 输出结果
        System.out.println("总条数:" + userPage.getTotal());
        System.out.println("总页数:" + userPage.getPages());
        userPage.getRecords().forEach(System.out::println);
    }
}

三、Mybatis-Plus的功能使用

3.1、BaseMapper<User>

在mybatis中如果不想写xml中的表的curd代码,就可以使用mybatisplus中提供的继承BaseMapper<User>,然后在mapper接口层中去继承。

注意:要指定实体类泛型,只有指定了泛型,mybatisplus才知道操作哪个实体的curd,下面我以user实体为例

java 复制代码
package com.itheima.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface UserMapper extends BaseMapper<User> {
    void saveUser(User user);
    void deleteUser(Long id);
    void updateUser(User user);
    User queryUserById(@Param("id") Long id);
    List<User> queryUserByIds(@Param("ids") List<Long> ids);
}

3.2、常见注解

在mybatisplus中有一些约定如下:

  1. 在实体类里面,类名驼峰转下划线作为表名
  2. 名为id的字段作为主键
  3. 变量名驼峰转下划线作为表的字段名

如果实体和表的关系不符合上述mp的约定,那么就需要自己去定义表名和字段名,那么如何去定义呢?答案是

@TableName:用来指定表名,

@TableId用来指定表中的主键字段信息,如果不添加type的值,默认type=ASSIGN_ID,生成long类型的id

@TableFiled用来指定表中普通字段信息

注意:

  • 以属性名is开头且是bool类型,就算符合约定也需要加上@TableField主键
  • 成员变量名与数据库关键字冲突时,要加上@TableField注解,且里面的值要用反引号引起来
  • 成员变量不是字段值,需要加上@TableField,且指定exist=false。

3.3、常见配置

注意:配置中id-type如果是assign_id,而类注解上@TableId("",type=IdType.Auto),那么由于注解优先级高于全局配置优先级,所以type=IdType.Auto会生效,但是如果没有指定的话,那么全局配置将生效

3.4、条件构造器

除了新增以外,修改、删除、查询的SQL语句都需要指定where条件。因此mybatis-plus中BaseMapper提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件。

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

接下来,我们就来看看如何利用Wrapper实现复杂查询。

3.4.1、QueryWrapper

无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件。

接下来看一些例子:

查询 :查询出名字中带o的,存款大于等于1000元的人。代码如下:

java 复制代码
@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);
}

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

java 复制代码
@Test
void testUpdateByQueryWrapper() {
    // 1.构建查询条件 where name = "Jack"
    QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "Jack");
    // 2.更新数据,user中非null字段都会作为set语句
    User user = new User();
    user.setBalance(2000);
    userMapper.update(user, wrapper);
}

3.4.2、UpdateWrapper

基于BaseMapper中的update方法更新时只能直接赋值,对于一些复杂的需求就难以实现。 例如:更新id为1,2,4的用户的余额,扣200,对应的SQL应该是:

java 复制代码
UPDATE user SET balance = balance - 200 WHERE id in (1, 2, 4)

SET的赋值结果是基于字段现有值的,这个时候就要利用UpdateWrapper中的setSql功能了:

java 复制代码
@Test
void testUpdateWrapper() {
    List<Long> ids = List.of(1L, 2L, 4L);
    // 1.生成SQL
    UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
            .setSql("balance = balance - 200") // SET balance = balance - 200
            .in("id", ids); // WHERE id in (1, 2, 4)
        // 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,
    // 而是基于UpdateWrapper中的setSQL来更新
    userMapper.update(null, wrapper);
}

3.4.3、LambdaQueryWrapper

无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值。这在编程规范中显然是不推荐的。 那怎么样才能不写字段名,又能知道字段名呢?

其中一种办法是基于变量的**gettter** 方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用Lambda表达式。 因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:

  • LambdaQueryWrapper

  • LambdaUpdateWrapper

分别对应QueryWrapper和UpdateWrapper

其使用方式如下:

java 复制代码
@Test
void testLambdaQueryWrapper() {
    // 1.构建条件 WHERE username LIKE "%o%" AND balance >= 1000
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.lambda()
            .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);
}

3.5、自定义SQL

先来看一个案例代码:

这种写法在一些企业也是不允许的,因为SQL语句最好都维护在持久层,而不是业务层。就当前案例来说,由于条件是in语句,只能将SQL写在Mapper.xml文件,利用foreach来生成动态SQL。 这实在是太麻烦了。假如查询条件更复杂,动态SQL的编写也会更加复杂。

所以,MybatisPlus提供了自定义SQL功能,可以让我们利用Wrapper生成查询条件,再结合Mapper.xml编写SQL

java 复制代码
@Test
void testCustomWrapper() {
    // 1.准备自定义查询条件
    List<Long> ids = List.of(1L, 2L, 4L);
    QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);

    // 2.调用mapper的自定义方法,直接传递Wrapper
    userMapper.deductBalanceByIds(200, wrapper);
}

然后在UserMapper中自定义SQL:

java 复制代码
package com.itheima.mp.mapper;

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

public interface UserMapper extends BaseMapper<User> {
    @Select("UPDATE user SET balance = balance - #{money} ${ew.customSqlSegment}")
    void deductBalanceByIds(@Param("money") int money, @Param("ew") QueryWrapper<User> wrapper);
}

3.6、Iservice接口

3.6.1、基本用法

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

  • save:新增

  • remove:删除

  • update:更新

  • get:查询单个结果

  • list:查询集合结果

  • count:计数

  • page:分页查询

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

java 复制代码
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> {
    // 拓展自定义方法
}
java 复制代码
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 {
}

测试代码:

java 复制代码
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.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Api(tags = "用户管理接口")
@RequiredArgsConstructor
@RestController
@RequestMapping("users")
public class UserController {

    private final IUserService userService;

    @PostMapping
    @ApiOperation("新增用户")
    public void saveUser(@RequestBody UserFormDTO userFormDTO){
        // 1.转换DTO为PO
        User user = BeanUtil.copyProperties(userFormDTO, User.class);
        // 2.新增
        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){
        // 1.查询用户
        User user = userService.getById(userId);
        // 2.处理vo
        return BeanUtil.copyProperties(user, UserVO.class);
    }

    @GetMapping
    @ApiOperation("根据id集合查询用户")
    public List<UserVO> queryUserByIds(@RequestParam("ids") List<Long> ids){
        // 1.查询用户
        List<User> users = userService.listByIds(ids);
        // 2.处理vo
        return BeanUtil.copyToList(users, UserVO.class);
    }
}

不过,一些带有业务逻辑的接口则需要在service中自定义实现了。例如下面的需求:

  • 根据id扣减用户余额

这看起来是个简单修改功能,只要修改用户余额即可。但这个业务包含一些业务逻辑处理:

  • 判断用户状态是否正常

  • 判断用户余额是否充足

这些业务逻辑都要在service层来做,另外更新余额需要自定义SQL,要在mapper中来实现。因此,我们除了要编写controller以外,具体的业务还要在service和mapper中编写。

首先在UserController中定义一个方法:

java 复制代码
@PutMapping("{id}/deduction/{money}")
@ApiOperation("扣减用户余额")
public void deductBalance(@PathVariable("id") Long id, @PathVariable("money")Integer money){
    userService.deductBalance(id, money);
}

然后是UserService接口:

java 复制代码
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实现类:

java 复制代码
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.deductMoneyById(id, money);
    }
}

最后是mapper:

java 复制代码
@Update("UPDATE user SET balance = balance - #{money} WHERE id = #{id}")
void deductMoneyById(@Param("id") Long id, @Param("money") Integer money);

3.6.2、Lambda

IService中还提供了Lambda功能来简化我们的复杂查询及更新功能。

案例一:实现复杂条件查询用户接口(根据name,status,minBalance,maxBalance,可以为空)做条件查询

java 复制代码
//实现复杂条件查询用户接口(根据name,status,minBalance,maxBalance,可以为空)做条件查询
@ApiOperation("lambdaQuery复杂条件查询用户接口")
@GetMapping("list")
private List<UserVO> listByIds(UserQuery userQuery){
    List<User> users = userService.getUserList(userQuery.getName(), userQuery.getStatus(), userQuery.getMinBalance(), userQuery.getMaxBalance());
    return BeanUtil.copyToList(users, UserVO.class);
}
@Override
public List<User> getUserList(String name, Integer status, Integer minBalance, Integer maxBalance) {
     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();
}

案例二:根据id实现扣减余额

java 复制代码
//IService的lambdaUpdate方法实现id扣减余额
@ApiOperation("lambdaUpdate方法实现id扣减余额")
@PutMapping("/{id}/lambdaUpdate/{money}")
private void lambdaUpdateById(@ApiParam ("用户id") @PathVariable Long id,@ApiParam("扣减余额") @PathVariable Long money){
    //sql:update user set balance = balance - #{money} where id = #{id};
    userService.lambdaUpdateById(id, money);
}
@Override
@Transactional
public void lambdaUpdateById(Long id, Long money) {
    //1、查询用户
    User user=getById(id);
    if(user==null||user.getStatus()==2){
        throw new RuntimeException("用户状态异常");
    }
    //2、判断余额状态
    if(user.getBalance()-money<0){
        throw new RuntimeException("余额不足");
    }
    long 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();
}

3.6.3、IService的批量新增

MybatisPlus的批处理是基于PrepareStatement的预编译模式,然后批量提交,最终在数据库执行时还是会有多条insert语句,逐条插入数据。SQL类似这样:

java 复制代码
Preparing: INSERT INTO user ( username, password, phone, info, balance, create_time, update_time ) VALUES ( ?, ?, ?, ?, ?, ?, ? )
Parameters: user_1, 123, 18688190001, "", 2000, 2023-07-01, 2023-07-01
Parameters: user_2, 123, 18688190002, "", 2000, 2023-07-01, 2023-07-01
Parameters: user_3, 123, 18688190003, "", 2000, 2023-07-01, 2023-07-01

而如果想要得到最佳性能,最好是将多条SQL合并为一条,像这样:

java 复制代码
INSERT INTO user ( username, password, phone, info, balance, create_time, update_time )
VALUES 
(user_1, 123, 18688190001, "", 2000, 2023-07-01, 2023-07-01),
(user_2, 123, 18688190002, "", 2000, 2023-07-01, 2023-07-01),
(user_3, 123, 18688190003, "", 2000, 2023-07-01, 2023-07-01),
(user_4, 123, 18688190004, "", 2000, 2023-07-01, 2023-07-01);

该怎么做呢?

MySQL的客户端连接参数中有这样的一个参数:rewriteBatchedStatements。顾名思义,就是重写批处理的statement语句。参考文档:

https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-connp-props-performance-extensions.html#cj-conn-prop_rewriteBatchedStatements

这个参数的默认值是false,我们需要修改连接参数,将其配置为true

修改项目中的application.yml文件,在jdbc的url后面添加参数&rewriteBatchedStatements=true:

XML 复制代码
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: MySQL123

再次测试插入数据,可以发现速度有非常明显的提升

3.7、代码生成

在使用MybatisPlus以后,基础的MapperServicePO代码相对固定,重复编写也比较麻烦。因此MybatisPlus官方提供了代码生成器根据数据库表结构生成POMapperService等相关代码。只不过代码生成器同样要编码使用,也很麻烦。

这里推荐大家使用一款MybatisPlus的插件,它可以基于图形化界面完成MybatisPlus的代码生成,非常简单。

3.7.1、安装插件

Idea的plugins市场中搜索并安装MyBatisPlus插件:

3.7.2、使用

首先需要配置数据库地址,在Idea顶部菜单中,找到other,选择Config Database

在弹出的窗口中填写数据库连接的基本信息:

点击OK保存。

然后再次点击Idea顶部菜单中的other,然后选择Code Generator:

在弹出的表单中填写信息:

最终,代码自动生成到指定的位置了。

3.8、静态工具

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

实例:

XML 复制代码
@Test
void testDbGet() {
    User user = Db.getById(1L, User.class);
    System.out.println(user);
}

@Test
void testDbList() {
    // 利用Db实现复杂条件查询
    List<User> list = Db.lambdaQuery(User.class)
            .like(User::getUsername, "o")
            .ge(User::getBalance, 1000)
            .list();
    list.forEach(System.out::println);
}

@Test
void testDbUpdate() {
    Db.lambdaUpdate(User.class)
            .set(User::getBalance, 2000)
            .eq(User::getUsername, "Rose");
}

3.9、逻辑删除

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

  • 在表中添加一个字段标记数据是否被删除

  • 当删除数据时把标记置为true

  • 查询时过滤掉标记为true的数据

一旦采用了逻辑删除,所有的查询和删除逻辑都要跟着变化,非常麻烦。

为了解决这个问题,MybatisPlus就添加了对逻辑删除的支持。

注意,只有MybatisPlus生成的SQL语句才支持自动的逻辑删除,自定义SQL需要自己手动处理逻辑删除。

此时需要给表和实体类额外添加一个逻辑删除字段

配置逻辑删除字段

XML 复制代码
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)

开启了逻辑删除功能以后,我们就可以像普通删除一样做CRUD,基本不用考虑代码逻辑问题。还是非常方便的。

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

  • 会导致数据库表垃圾数据越来越多,从而影响查询效率

  • SQL中全都需要对逻辑删除字段做判断,影响查询效率

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

3.10、通用枚举

一般User类中都会有一个Status代表状态的属性,像这种字段我们一般会定义一个枚举,做业务判断的时候就可以直接基于枚举做比较。但是我们数据库采用的是int类型,对应的PO也是Integer。因此业务操作时必须手动把枚举Integer转换,非常麻烦。

因此,MybatisPlus提供了一个处理枚举的类型转换器,可以帮我们 枚举类型与数据库类型自动转换

定义枚举类:

XML 复制代码
package com.itheima.mp.enums;

import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;

@Getter
public enum UserStatus {
    NORMAL(1, "正常"),
    FREEZE(2, "冻结")
    ;
    @EnumValue
    private final int value;
    private final String desc;

    UserStatus(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }
}

要让MybatisPlus处理枚举与数据库类型自动转换,我们必须告诉MybatisPlus,枚举中的哪个字段的值作为数据库值。 MybatisPlus提供了@EnumValue注解来标记枚举属性:

配置枚举处理类

XML 复制代码
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

3.11、JSON类型处理器

数据库的user表中有一个info字段,是JSON类型

格式为**:{"age": 20, "intro": "佛系青年", "gender": "male"}**

而一般类中都是用String类型去存储的,

这样一来,我们要读取info中的属性时就非常不方便。如果要方便获取,info的类型最好是一个Map或者实体类。

而一旦我们把info改为对象类型,就需要在写入数据库时手动转为String,再读取数据库时,手动转换为对象,这会非常麻烦。

因此MybatisPlus提供了很多特殊类型字段的类型处理器,解决特殊字段类型与数据库类型转换的问题。例如处理JSON就可以使用JacksonTypeHandler处理器。

接下来,我们就来看看这个处理器该如何使用。

定义一个实体:

XML 复制代码
package com.itheima.mp.domain.po;

import lombok.Data;

@Data
public class UserInfo {
    private Integer age;
    private String intro;
    private String gender;
}

接下来,将User类的info字段修改为UserInfo类型,并声明类型处理器:

四、插件功能

MybatisPlus提供了很多的插件功能,进一步拓展其功能。目前已有的插件有:

  • PaginationInnerInterceptor:自动分页

  • TenantLineInnerInterceptor:多租户

  • DynamicTableNameInnerInterceptor:动态表名

  • OptimisticLockerInnerInterceptor:乐观锁

  • IllegalSQLInnerInterceptor:sql 性能规范

  • BlockAttackInnerInterceptor:防止全表更新与删除

4.1、分页插件

使用多个分页插件的时候需要注意插件定义顺序,建议使用顺序如下:

  • 多租户,动态表名

  • 分页,乐观锁

  • sql 性能规范,防止全表更新与删除

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

配置类:

java 复制代码
package com.itheima.mp.config;

import com.baomidou.mybatisplus.annotation.DbType;
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();
        // 添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

测试:

java 复制代码
@Test
void testPageQuery() {
    // 1.分页查询,new Page()的两个参数分别是:页码、每页大小
    Page<User> p = userService.page(new Page<>(2, 2));
    // 2.总条数
    System.out.println("total = " + p.getTotal());
    // 3.总页数
    System.out.println("pages = " + p.getPages());
    // 4.数据
    List<User> records = p.getRecords();
    records.forEach(System.out::println);
}

最后,看完请点个赞吧,谢谢🦀🦀

相关推荐
毕设源码-邱学长2 小时前
【开题答辩全过程】以 南工计算机等级网站为例,包含答辩的问题和答案
java
NE_STOP2 小时前
spring boot3--自动配置与手动配置
java
小北方城市网2 小时前
Spring Cloud Gateway 生产级微内核架构设计与可插拔过滤器开发
java·大数据·linux·运维·spring boot·redis·分布式
csdn_aspnet2 小时前
Java常用算法深度解析:从集合框架到并发编程
java·算法
sheji34162 小时前
【开题答辩全过程】以 基于j2ee的问卷调查系统为例,包含答辩的问题和答案
java·java-ee
计算机学姐2 小时前
基于SpringBoot的自习室座位预定系统【预约选座+日期时间段+协同过滤推荐算法+数据可视化统计】
java·vue.js·spring boot·后端·spring·信息可视化·tomcat
Thomas_YXQ2 小时前
Unity3D中提升AssetBundle加载速度的详细指南
java·spring boot·spring·unity·性能优化·游戏引擎·游戏开发
Marktowin3 小时前
控制权限系列之(2)手把手教你使用基于角色的权限控制
后端
码农水水3 小时前
京东Java面试被问:分布式会话的一致性和容灾方案
java·开发语言·数据库·分布式·mysql·面试·职场和发展