超详细的MyBatis Plus使用方法

328 阅读19分钟

一.Mybatis Plus简介

1.1 MyBatist 概述

MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 在这里插入图片描述

1.2 MyBatis特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

1.3 支持数据库

在这里插入图片描述

1.4 MyBatis Plus 的核心架构

在这里插入图片描述

二.MyBatis Plus快速入门

2.1 数据库准备

-- 导出 mp 的数据库结构
CREATE DATABASE IF NOT EXISTS `mp` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
USE `mp`;

-- 导出  表 mp.address 结构
CREATE TABLE IF NOT EXISTS `address` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `user_id` bigint DEFAULT NULL COMMENT '用户ID',
  `province` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '省',
  `city` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '市',
  `town` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '县/区',
  `mobile` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '手机',
  `street` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '详细地址',
  `contact` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '联系人',
  `is_default` bit(1) DEFAULT b'0' COMMENT '是否是默认 1默认 0否',
  `notes` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',
  `deleted` bit(1) DEFAULT b'0' COMMENT '逻辑删除',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `user_id` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=71 DEFAULT CHARSET=utf8mb3 ROW_FORMAT=COMPACT;

-- 正在导出表  mp.address 的数据:~11 rows (大约)

INSERT INTO `address` (`id`, `user_id`, `province`, `city`, `town`, `mobile`, `street`, `contact`, `is_default`, `notes`, `deleted`) VALUES
	(59, 2, '北京', '北京', '朝阳区', '13900112222', '金燕龙办公楼', 'Rose', b'1', NULL, b'0'),
	(60, 1, '北京', '北京', '朝阳区', '13700221122', '修正大厦', 'Jack', b'0', NULL, b'0'),
	(61, 1, '上海', '上海', '浦东新区', '13301212233', '航头镇航头路', 'Jack', b'1', NULL, b'0'),
	(63, 2, '广东', '佛山', '永春', '13301212233', '永春武馆', 'Rose', b'0', NULL, b'0'),
	(64, 3, '浙江', '杭州', '拱墅区', '13567809102', '浙江大学', 'Hope', b'1', NULL, b'0'),
	(65, 3, '浙江', '杭州', '拱墅区', '13967589201', '左岸花园', 'Hope', b'0', NULL, b'0'),
	(66, 4, '湖北', '武汉', '汉口', '13967519202', '天天花园', 'Thomas', b'1', NULL, b'0'),
	(67, 3, '浙江', '杭州', '拱墅区', '13967589201', '左岸花园', 'Hopey', b'0', NULL, b'0'),
	(68, 4, '湖北', '武汉', '汉口', '13967519202', '天天花园', 'Thomas', b'1', NULL, b'0'),
	(69, 3, '浙江', '杭州', '拱墅区', '13967589201', '左岸花园', 'Hopey', b'0', NULL, b'0'),
	(70, 4, '湖北', '武汉', '汉口', '13967519202', '天天花园', 'Thomas', b'1', NULL, b'0');

-- 导出  表 mp.user 结构
CREATE TABLE `user` (
	`id` BIGINT(19) NOT NULL AUTO_INCREMENT COMMENT '用户id',
	`username` VARCHAR(50) NOT NULL COMMENT '用户名' COLLATE 'utf8_general_ci',
	`password` VARCHAR(128) NOT NULL COMMENT '密码' COLLATE 'utf8_general_ci',
	`phone` VARCHAR(20) NULL DEFAULT NULL COMMENT '注册手机号' COLLATE 'utf8_general_ci',
	`info` JSON NOT NULL COMMENT '详细信息',
	`status` INT(10) NULL DEFAULT '1' COMMENT '使用状态(1正常 2冻结)',
	`balance` INT(10) NULL DEFAULT NULL COMMENT '账户余额',
	`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
	`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
	PRIMARY KEY (`id`) USING BTREE,
	UNIQUE INDEX `username` (`username`) USING BTREE
)
COMMENT='用户表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
ROW_FORMAT=COMPACT
AUTO_INCREMENT=5
;

-- 正在导出表  mp.user 的数据:~4 rows (大约)

INSERT INTO `user` (`id`, `username`, `password`, `phone`, `info`, `status`, `balance`, `create_time`, `update_time`) VALUES
	(1, 'Jack', '123', '13900112224', '{"age": 20, "intro": "佛系青年", "gender": "male"}', 1, 1600, '2023-05-19 20:50:21', '2023-06-19 20:50:21'),
	(2, 'Rose', '123', '13900112223', '{"age": 19, "intro": "青涩少女", "gender": "female"}', 1, 600, '2023-05-19 21:00:23', '2023-06-19 21:00:23'),
	(3, 'Hope', '123', '13900112222', '{"age": 25, "intro": "上进青年", "gender": "male"}', 1, 100000, '2023-06-19 22:37:44', '2023-06-19 22:37:44'),
	(4, 'Thomas', '123', '17701265258', '{"age": 29, "intro": "伏地魔", "gender": "male"}', 1, 800, '2023-06-19 23:44:45', '2023-06-19 23:44:45');

2.2 导入依赖

Spring Boot2

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.14</version>
</dependency>

Spring Boot3

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>3.5.14</version>
</dependency>

Spring Boot4 (自3.5.13开始)

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot4-starter</artifactId>
    <version>3.5.14</version>
</dependency>

2.3 编写实体类

package com.itheima.mp.domain.po;

import lombok.Data;

import java.time.LocalDateTime;

@Data
public class User {
    /** 用户id */
    private Long id;
    /** 用户名 */
    private String username;
    /** 密码 */
    private String password;
    /** 注册手机号 */
    private String phone;
    /** 详细信息 */
    private String info;
    /** 使用状态(1正常 2冻结)*/
    private Integer status;
    /** 账户余额 */
    private Integer balance;
    /** 创建时间 */
    private LocalDateTime createTime;
    /** 更新时间 */
    private LocalDateTime updateTime;
}

2.4 Mapper接口

我们自定义的Mapper只要实现了这个BaseMapper,就无需自己实现单表CRUD了,让其继承BaseMapper

package com.itheima.mp.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;

public interface UserMapper extends BaseMapper<User> {

}

2.5 application.yml

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: You-Password
logging:
  level:
    com.itheima: debug
  pattern:
    dateformat: HH:mm:ss
# 配置日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2.6 启动类

在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹

package com.itheima.mp;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("com.itheima.mp.mapper")
@SpringBootApplication
public class MpDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(MpDemoApplication.class, args);
    }

}

2.7 测试类

package com.itheima.mp.mapper;

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 java.util.List;

@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void testInsert() {
        User user = new User();
        user.setId(5L);
        user.setUsername("Lucy");
        user.setPassword("123");
        user.setPhone("18688990011");
        user.setBalance(200);
        user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        userMapper.insert(user);
    }

    @Test
    void testSelectById() {
        User user = userMapper.selectById(5L);
        System.out.println("user = " + user);
    }


    @Test
    void testQueryByIds() {
        List<User> users = userMapper.selectByIds(List.of(1L, 2L, 3L, 4L));
        users.forEach(System.out::println);
    }

    @Test
    void testUpdateById() {
        User user = new User();
        user.setId(5L);
        user.setBalance(20000);
        userMapper.updateById(user);
    }

    @Test
    void testDeleteUser() {
        userMapper.deleteById(5L);
    }
}

三. MyBatis Plus的核心功能

3.1常见注解

3.1.1 @TableName

该注解用于指定实体类对应的数据库表名。当实体类名与数据库表名不一致,或者实体类名不是数据库表名的驼峰写法时,您需要使用这个注解来明确指定表名。

@TableName("sys_user")
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

TableName注解除了指定表名以外,还可以指定很多其它属性:

在这里插入图片描述

3.1.2 @TableId

该注解用于标记实体类中的主键字段。如果你的主键字段名为 id,你可以省略这个注解。

@TableName("sys_user")
public class User {
    @TableId
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

TableId注解支持两个属性:

在这里插入图片描述

IdType支持的类型有:

在这里插入图片描述

3.1.3 @TableField

该注解用于标记实体类中的非主键字段,它告诉 MyBatis-Plus 如何映射实体类字段到数据库表字段。如果你的实体类字段名遵循驼峰命名规则,并且与数据库表字段名一致,你可以省略这个注解。

@TableName("sys_user")
public class User {
    @TableId
    private Long id;
    @TableField("nickname") // 映射到数据库字段 "nickname"
    private String name;
    private Integer age;
    private String email;
}

一般情况下我们并不需要给字段添加@TableField注解,一些特殊情况除外:

  • 成员变量名与数据库字段名不一致
  • 成员变量是以isXXX命名,按照JavaBean的规范,MybatisPlus识别字段时会把is去除,这就导致与数据库不符。
  • 成员变量名与数据库一致,但是与数据库的关键字冲突。使用@TableField注解给字段名添加转义字符:``

在这里插入图片描述在这里插入图片描述

了解注解详细及其他注解,见MyBatisPlus官网

3.2 Wrapper条件构造器

MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。

参数中的Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图: 在这里插入图片描述

Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法: 在这里插入图片描述

QueryWrapper常见的API,详解见官网

eq( )   # 等于 =
ne( )   # 不等于 <> 或者 !=
gt( )   # 大于 >
ge( )   # 大于等于  >=
lt( )   # 小于 <
le( )   # 小于等于 <=
between( )   # BETWEEN 值1 AND 值2 
notBetween( )  # NOT BETWEEN 值1 AND 值2 
in( )  # in
notIn( ) # not in
like()  # like 模糊查询,左右都加%
likeRight() # 只有右边加%
likeLeft() # 只有左边加%
in() # 字段 IN (v0, v1, …)
notIn() # 字段 NOT IN (v0, v1, …)
......

3.2.1 QueryWrapper

专门用于构造查询条件,支持基本的等于、不等于、大于、小于等各种常见操作。它允许你以链式调用的方式添加多个查询条件,并且可以组合使用 and 和 or 逻辑。

无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件。接下来看一些例子: 查询:查询出名字中带o的,存款大于等于1000元的人。代码如下:

@Test
    void testQueryWrapper() {
        // 构建查询条件
        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .select("id", "username", "info", "balance")
                .like("username", "o")
                .ge("balance", 1000);
        // 查询
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }

更新:更新用户名为Hope的用户的余额为20000,代码如下:

@Test
    void testUpdateByQueryWrapper() {
        // 构建更新条件
        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .eq("username", "Hope");
        // 构建更新数据
        User user = new User();
        user.setBalance(20000);
        // 更新
        int update = userMapper.update(user, wrapper);
    }

3.2.2 UpdateWrapper

用于构造更新条件,可以在更新数据时指定条件。与 QueryWrapper 类似,它也支持链式调用和逻辑组合。使用 UpdateWrapper 可以在不创建实体对象的情况下,直接设置更新字段和条件。

更新id为1,2,4的用户的余额,扣200

@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.2.3 LambdaQueryWrapper

这是一个基于 Lambda 表达式的查询条件构造器,它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名。这种方式提高了代码的可读性和可维护性,尤其是在字段名可能发生变化的情况下。

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

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

  • LambdaQueryWrapper
  • LambdaUpdateWrapper 分别对应QueryWrapper和UpdateWrapper
@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.3 自定义Sql

更新id为1,2,4的用户的余额,扣200

@Test
    void testCustomSqlUpdate() {
        // 更新条件
        List<Long> ids = List.of(1L, 2L, 4L);
        int amount = 200;
        // 定义条件
        QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);
        // 自定义sql方法
        userMapper.updateBalanceByIds(wrapper, amount);
    }

然后在UserMapper中自定义SQL:

// @Param(Constants.WRAPPER) 是固定写法,'ew'是Wrapper的默认别名
void updateBalanceByIds(@Param("ew") QueryWrapper<User> wrapper, @Param("amount") int amount);

最后在UserMapper.xml中定义SQL

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mp.mapper.UserMapper">
    <update id="updateBalanceByIds">
        update user set balance = balance - #{amount} ${ew.customSqlSegment}
    </update>
</mapper>

确保你的 MyBatis-Plus 配置中指定了 XML 文件的位置(通常默认配置就是正确的):

mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml

3.4 Service接口

IService 是 MyBatis-Plus 提供的一个通用 Service 层接口,它封装了常见的 CRUD 操作,包括插入、删除、查询和分页等。通过继承 IService 接口,可以快速实现对数据库的基本操作,同时保持代码的简洁性和可维护性。

3.3.1 Service常用方法

新增:

save:新增单个元素 saveBatch:批量新增 saveOrUpdate:根据id判断,如果数据存在就更新,不存在则新增 saveOrUpdateBatch:批量的新增或修改

删除:

removeById:根据id删除 removeByIds:根据id批量删除 removeByMap:根据Map中的键值对为条件删除 remove(Wrapper):根据Wrapper条件删除 removeBatchByIds:暂不支持

修改:

updateById:根据id修改 update(Wrapper):根据UpdateWrapper修改,Wrapper中包含set和where部分 update(T,Wrapper):按照T内的数据修改与Wrapper匹配到的数据 updateBatchById:根据id批量修改

查询:

getById:根据id查询1条数据 getOne(Wrapper):根据Wrapper查询1条数据 getBaseMapper:获取Service内的BaseMapper实现,某些时候需要直接调用Mapper内的自定义SQL时可以用这个方法获取到Mapper listByIds:根据id批量查询 list(Wrapper):根据Wrapper条件查询多条数据 list():查询所有 count():统计所有数量 count(Wrapper):统计符合Wrapper条件的数据数量

3.3.2 Service基本用法

定义一个UserService接口继承与MyBatisPlus提供的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> {
}

定义一个UserService的实现类,并且继承与MyBatisPlus提供的ServiceImpl:

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 {
}

四. MyBatis Plus的扩展功能

4.1 MyBatis Plus代码生成器插件

安装MyBatis Plus插件

在这里插入图片描述

我用的是2024的idea,Config Database和Code Genertor在导航栏工具里

在这里插入图片描述

有些版本是直接在导航栏里

在这里插入图片描述

点击这两个选项分别配置好:(配置你自己的数据库)

在这里插入图片描述

根据需求自行选择即可

在这里插入图片描述

4.2 静态工具

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

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

4.3 逻辑删除

逻辑删除是一种优雅的数据管理策略,它通过在数据库中标记记录为“已删除”而非物理删除,来保留数据的历史痕迹,同时确保查询结果的整洁性。MyBatis-Plus 提供了便捷的逻辑删除支持,使得这一策略的实施变得简单高效。

逻辑删除的工作原理: 插入:逻辑删除字段的值不受限制。 查找:自动添加条件,过滤掉标记为已删除的记录。 更新:防止更新已删除的记录。 删除:将删除操作转换为更新操作,标记记录为已删除。

方法 1: 配置全局逻辑删除属性 在 application.yml 中配置 MyBatis-Plus 的全局逻辑删除属性:

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted # 全局逻辑删除字段名
      logic-delete-value: 1 # 逻辑已删除值。可选,默认值为 1
      logic-not-delete-value: 0 # 逻辑未删除值。可选,默认值为 0

方法 2: 如不想使用全局配置,可以在实体类中使用 @TableLogic 注解,对类单独进行配置

import com.baomidou.mybatisplus.annotation.TableLogic;

public class User {
    // 其他字段...
    
    @TableLogic
    private Integer deleted;
}

同样,逻辑未删除值默认为 0,逻辑已删除值默认为 1。这两个值可以通过设置 @TableLogic 注解的 value 属性和 delval 属性的值来进行修改。

在这里插入图片描述

4.4 枚举处理器

4.4.1 定义枚举

方式一:注解标记

枚举属性使用 @EnumValue 注解,指定枚举值在数据库中存储的实际值。支持枚举类中的任意字段,如序号或编码。

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

方式二:实现接口

实现 IEnum 接口,实现 getValue 方法,指定枚举值在数据库中存储的实际值。支持枚举类中的任意字段,如序号或编码。

@Getter
@AllArgsConstructor
public enum AgeEnum implements IEnum<Integer> {
    ONE(1, "一岁"),
    TWO(2, "二岁"),
    THREE(3, "三岁");

    private final int value;
    private final String desc;

    @Override
    public Integer getValue() {
        return this.value;
    }
}

4.4.2 配置枚举处理器

yml 配置文件中配置:

mybatis-plus:
  configuration:
    default-enum-type-handler: xx.xx.xx.MyEnumTypeHandler

自定义配置类:

@Configuration
public class MybatisPlusAutoConfiguration {

    @Bean
    public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() {
        return properties -> {
            GlobalConfig globalConfig = properties.getGlobalConfig();
            globalConfig.setBanner(false);
            MybatisPlusProperties.CoreConfiguration configuration = new MybatisPlusProperties.CoreConfiguration();
            configuration.setDefaultEnumTypeHandler(MyEnumTypeHandler.class);
            properties.setConfiguration(configuration);
        };
    }
}

4.5 字段类型处理器

在 MyBatis 中,类型处理器(TypeHandler)扮演着 JavaType 与 JdbcType 之间转换的桥梁角色。它们用于在执行 SQL 语句时,将 Java 对象的值设置到 PreparedStatement 中,或者从 ResultSet 或 CallableStatement 中取出值。

MyBatis-Plus 给大家提供了提供了一些内置的类型处理器,可以通过 TableField 注解快速注入到 MyBatis 容器中,从而简化开发过程。

配置 @TableName, @TableField

@Data
@Accessors(chain = true)
@TableName(autoResultMap = true)
public class User {
    private Long id;
    /**
     * 必须开启映射注解
     * @TableName(autoResultMap = true)
     * 
     * 选择对应的 JSON 处理器,并确保存在对应的 JSON 解析依赖包
     */
    @TableField(typeHandler = JacksonTypeHandler.class)
    // 或者使用 FastjsonTypeHandler
    // @TableField(typeHandler = FastjsonTypeHandler.class)
    private OtherInfo otherInfo;
}

XML 配置对应写法: 在 XML 映射文件中,可以使用 result 元素来指定字段的类型处理器。

<!-- 单个字段的类型处理器配置 -->
<result column="other_info" jdbcType="VARCHAR" property="otherInfo" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />

<!-- 多个字段中某个字段的类型处理器配置 -->
<resultMap id="departmentResultMap" type="com.baomidou...DepartmentVO">
    <result property="director" column="director" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
</resultMap>
<select id="selectPageVO" resultMap="departmentResultMap">
   select id,name,director from department ...
</select>

Wrapper 查询中的 TypeHandler 使用 从 MyBatis-Plus 3.5.3.2 版本开始,可以在 Wrapper查询中直接使用 TypeHandler。

Wrappers.<H2User>lambdaQuery()
    .apply("name={0,typeHandler=" + H2userNameJsonTypeHandler.class.getCanonicalName() + "}", "{\"id\":101,\"name\":\"Tomcat\"}"))

字段类型处理器还可以自定义,详解见MaBatis Plus官网

4.6 数据安全(加密)

MyBatis-Plus 提供了数据安全保护功能,旨在防止因开发人员流动而导致的敏感信息泄露。从3.3.2版本开始,MyBatis-Plus 支持通过加密配置和数据安全措施来增强数据库的安全性。

YML 配置加密 MyBatis-Plus 允许你使用加密后的字符串来配置数据库连接信息。在 YML 配置文件中,以 mpw: 开头的配置项将被视为加密内容。

spring:
  datasource:
    url: mpw:qRhvCwF4GOqjessEB3G+a5okP+uXXr96wcucn2Pev6Bf1oEMZ1gVpPPhdDmjQqoM
    password: mpw:Hzy5iliJbwDHhjLs1L0j6w==
    username: mpw:Xb+EgsyuYRXw7U7sBJjBpA==

密钥加密 使用 AES 算法生成随机密钥,并对敏感数据进行加密。

// 生成16位随机AES密钥
String randomKey = AES.generateRandomKey();

// 使用随机密钥加密数据
String encryptedData = AES.encrypt(data, randomKey);

如何使用 在启动应用程序时,通过命令行参数。 自3.5.10开始支持系统属性与环境变量传递密钥.

// Jar 启动参数示例(在IDEA中设置Program arguments,或在服务器上设置为启动环境变量)
--mpw.key=d1104d7c3b616f0b

五. MyBatis Plus插件功能

5.1 分页插件

MyBatis-Plus 的分页插件 PaginationInnerInterceptor 提供了强大的分页功能,支持多种数据库,使得分页查询变得简单高效。

配置方法: 在 Spring Boot 项目中,你可以通过 Java 配置来添加分页插件:

@Configuration
@MapperScan("scan.your.mapper.package") // mapper接口的所在位置
public class MybatisPlusConfig {

    /**
     * 添加分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
         // 如果配置多个插件, 切记分页最后添加
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        // 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
        return interceptor;
    }
}

PaginationInnerInterceptor 提供了以下属性来定制分页行为:

在这里插入图片描述

自定义 Mapper 方法中使用分页

IPage<UserVo> selectPageVo(IPage<?> page, Integer state);
// 或者自定义分页类
MyPage selectPageVo(MyPage page);
// 或者返回 List
List<UserVo> selectPageVo(IPage<UserVo> page, Integer state);

对应的 XML 配置:

<select id="selectPageVo" resultType="xxx.xxx.xxx.UserVo">
    SELECT id,name FROM user WHERE state=#{state}
</select>

如果返回类型是 IPage,则入参的 IPage 不能为 null。如果想临时不分页,可以在初始化 IPage 时 size 参数传入小于 0 的值。 如果返回类型是 List,则入参的 IPage 可以为 null,但需要手动设置入参的 IPage.setRecords(返回的 List)。 如果 XML 需要从 page 里取值,需要使用 page.属性 获取。

Page 类继承了 IPage 类,实现了简单分页模型。如果你需要实现自己的分页模型,可以继承 Page 类或实现 IPage 类。

在这里插入图片描述

5.2 其他插件

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