作为Java后端开发者,我们都知道MyBatis是一款优秀的持久层框架,但它的XML配置繁琐、单表CRUD代码重复等问题,往往会占用我们大量的开发时间。而MyBatis-Plus(简称MP)的出现,完美解决了这些痛点——它是MyBatis的增强工具,在MyBatis基础上只做增强不做改变,既能保留MyBatis的灵活性,又能极大简化开发、提高效率,成为目前国内后端开发中最流行的持久层框架之一。

本文将从MyBatis-Plus概述、快速入门、日志配置、常用注解、Mapper CRUD接口、条件构造器等核心模块,结合具体案例详细讲解,全程干货无冗余,无论是新手入门还是老手查漏补缺,都能有所收获。

一、MyBatis-Plus概述

1. 简介

MyBatis-Plus是由国内团队「苞米豆(baomidou)」开发的MyBatis增强工具,官网地址:https://baomidou.com/

其核心定位是「增强不改变」,即引入MP后,不会对现有MyBatis工程产生任何侵入性影响,开发者依然可以使用MyBatis的原生语法,同时能享受MP提供的各种便捷功能,彻底摆脱重复的CRUD代码编写。

关于多表操作的解决方案,MP提供两种常用思路:

  • 方式一:沿用MyBatis的原生方式,编写一对一、一对多、多对多的关联查询SQL;

  • 方式二:在Service层通过多次单表条件查询,再通过代码逻辑组装关联数据(适合中小项目,简化SQL编写)。

2. 核心特性(必记)

MP的强大之处,在于它内置了大量实用功能,无需额外开发,开箱即用:

  • 无侵入:只做增强不做改变,引入后不影响现有工程,无缝衔接MyBatis;

  • 损耗小:启动时自动注入基本CRUD方法,性能几乎无损耗,直接面向对象操作;

  • 强大的CRUD操作:内置通用Mapper、通用Service,少量配置即可实现单表大部分CRUD,搭配条件构造器满足各类查询需求;

  • Lambda形式调用:通过Lambda表达式编写查询条件,避免字段名写错的问题,代码更简洁;

  • 主键自动生成:支持4种主键策略(含分布式唯一ID生成器Sequence),完美解决主键生成难题;

  • ActiveRecord模式:实体类继承Model类即可实现强大的CRUD操作,无需编写Mapper接口;

  • 自定义全局通用操作:一次编写,全局复用,减少重复代码;

  • 内置代码生成器:通过代码或Maven插件,快速生成Mapper、Model、Service、Controller层代码,支持模板自定义;

  • 内置分页插件:基于MyBatis物理分页,配置简单,写分页等同于普通List查询,支持多种数据库;

  • 性能分析插件:开发测试时可输出SQL语句及执行时间,快速定位慢查询;

  • 全局拦截插件:智能阻断全表delete、update操作,可自定义拦截规则,预防误操作。

3. 支持的数据库

任何能使用MyBatis进行CRUD且支持标准SQL的数据库,MP都能支持,包括但不限于:

MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer、Phoenix、Gauss、ClickHouse、Sybase、OceanBase等;

同时支持国内主流数据库:达梦、虚谷、人大金仓、南大通用、神通、瀚高数据库等。

4. 框架结构与执行流程

核心结构

MyBatis-Plus的核心结构分为4部分,层层递进,支撑起整个框架的功能:

  • MyBatis-Plus场景启动器:负责整合Spring Boot与MP,简化配置;

  • 注解组件:提供各类注解(如@TableName、@TableId),实现实体与数据库表的映射;

  • 核心组件:封装CRUD、条件构造、分页等核心功能;

  • 代码生成组件:快速生成各层代码,提升开发效率。

执行流程(核心)

MP的核心执行逻辑的是“自动生成SQL并注入MyBatis容器”,具体流程如下:

  1. 启动项目时,MP扫描指定包下的实体类;

  2. 通过Java反射机制,提取实体类的表名、字段名、字段类型等信息;

  3. 根据提取的信息,自动生成insert、update、delete、select等SQL语句;

  4. 将生成的SQL注入到MyBatis容器中,开发者直接调用方法即可执行。

二、MyBatis-Plus快速入门(实战案例)

本案例基于Spring Boot + Maven + MySQL,实现User表的CRUD操作,全程无XML配置,新手可直接跟着操作。

参考官网快速入门文档:https://baomidou.com/pages/24112f/

前置条件

在开始前,请确保你已具备以下环境和知识:

  • Java开发环境(JDK 8及以上)及IDE(IDEA推荐);

  • 熟悉Spring Boot基础;

  • 熟悉Maven依赖管理;

  • MySQL数据库(5.7及以上)。

实现步骤(共7步)

步骤1:创建数据库和表

首先创建数据库(本文命名为boot),然后创建user表并插入测试数据,SQL语句如下:

DROP TABLE IF EXISTS user;
CREATE TABLE user
(
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    PRIMARY KEY (id)
);
DELETE FROM user;
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
步骤2:创建Spring Boot项目

使用IDEA创建Spring Boot项目,初始化时选择Lombok、Spring Web依赖(后续手动添加MP和MySQL驱动依赖)。

步骤3:导入依赖(关键)

在pom.xml中添加MySQL驱动、Lombok、MyBatis-Plus启动器依赖,注意:尽量不要同时导入MyBatis和MyBatis-Plus,避免冲突。

<!-- 引入mysql的驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- 引入lombok的依赖 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<!-- 引入mybatis-plus的依赖-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>
步骤4:配置数据库连接

在application.properties文件中配置MySQL连接信息,替换自己的数据库用户名和密码:

# DataSource Config
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/boot?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: root
步骤5:编写实体类(POJO)

创建com.mp.pojo包,编写User实体类,使用Lombok的@Data注解简化getter/setter方法:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
步骤6:编写Mapper接口

创建com.mp.mapper包,编写UserMapper接口,只需继承BaseMapper<User>,即可获得MP内置的所有CRUD方法,无需编写任何SQL。

@Mapper // mybatis的注解,标识该接口为Mapper接口
public interface UserMapper extends BaseMapper<User> {
    // 无需编写任何方法,BaseMapper已内置所有CRUD功能
}
步骤7:编写启动类并测试

在启动类上添加@MapperScan注解,指定Mapper接口的扫描路径(如果Mapper接口添加了@Mapper注解,也可省略@MapperScan):

@MapperScan("com.mp.mapper") // 扫描Mapper接口所在包
@SpringBootApplication
public class SmpApplication {
    public static void main(String[] args) {
        SpringApplication.run(SmpApplication.class, args);
    }
}
测试CRUD(关键)

创建测试类(在test目录下),注入UserMapper,测试查询所有用户的功能:

@SpringBootTest
class SmpApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        // selectList(null) 表示无任何查询条件,查询所有数据
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }
}
测试结果

运行测试方法,控制台会输出所有用户信息,说明MP整合成功:

User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)
小结

通过以上7步,我们无需编写任何XML文件,也无需手动编写CRUD方法,就实现了User表的查询功能。这就是MP的强大之处——简化开发,让开发者专注于业务逻辑,而非重复的CRUD代码。

注意:如果实体类名与表名不一致,需在实体类上添加@TableName("表名")注解;如果实体类字段与表中列名不一致,需在字段上添加@TableField("列名")注解。

三、配置日志(查看SQL执行过程)

默认情况下,MP执行的SQL语句是不可见的,为了方便开发调试、查看SQL执行细节(如参数、执行时间),我们需要配置日志,让SQL语句打印到控制台。

配置方式(application.properties)

# 配置MyBatis-Plus日志(打印SQL到控制台)
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

配置后效果

再次运行测试方法,控制台会输出SQL执行过程,包括SQL语句、参数、执行时间等信息,例如:

==>  Preparing: SELECT id,name,age,email FROM user
==> Parameters: 
<==    Columns: id, name, age, email
<==        Row: 1, Jone, 18, test1@baomidou.com
<==        Row: 2, Jack, 20, test2@baomidou.com
<==        Row: 3, Tom, 28, test3@baomidou.com
<==        Row: 4, Sandy, 21, test4@baomidou.com
<==        Row: 5, Billie, 24, test5@baomidou.com
<==      Total: 5

通过日志,我们可以快速定位SQL问题,比如字段写错、条件错误等,极大提升调试效率。

四、MyBatis-Plus常用注解(必掌握)

MP提供了一系列注解,用于实现实体类与数据库表、字段的映射,解决命名不一致、主键策略等问题,以下是最常用的8个注解,重点掌握前4个。

1. @TableName(表名注解)

「作用」:标识实体类对应的数据库表名,当实体类名与表名不一致时必须使用。

「使用位置」:实体类类名上

// 示例:实体类名User,数据库表名sys_user
@TableName("sys_user")
public class User {
    private Long id;
    private String name;
    // 其他字段...
}

「核心属性」:

  • value:表名(必填,当实体类名与表名一致时可省略);

  • keepGlobalPrefix:是否保持使用全局tablePrefix(若全局配置了表前缀,且自行设置了value,可控制是否拼接前缀);

  • autoResultMap:是否自动构建resultMap(默认false,复杂字段映射时可开启)。

2. @TableId(主键注解)

「作用」:标识实体类的主键字段,用于指定主键策略。

「使用位置」:实体类主键字段上

public class User {
    // 主键字段,指定主键策略为数据库自增
    @TableId(type = IdType.AUTO)
    private Long id;
    // 其他字段...
}

「核心属性」:

  • value:主键字段名(当实体类字段与表中主键列名一致时可省略);

  • type:主键策略(重点,默认IdType.NONE,跟随全局配置)。

「主键策略(IdType)详解」:

  • AUTO:数据库ID自增(需数据库表主键设置为自增);

  • NONE:未设置主键类型,跟随全局配置;

  • INPUT:插入前自行设置主键值(手动输入);

  • ASSIGN_ID:分配ID(支持Long、Integer、String类型),使用雪花算法(默认);

  • ASSIGN_UUID:分配UUID(主键类型必须为String);

  • ID_WORKER/UUID/ID_WORKER_STR:已过时,推荐使用ASSIGN_ID/ASSIGN_UUID。

3. @TableField(字段注解,非主键)

「作用」:标识实体类的非主键字段,解决实体字段与表中列名不一致、字段是否为数据库表字段等问题。

「使用位置」:实体类非主键字段上

public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    // 实体字段name,数据库列名username
    @TableField("username")
    private String name;
    // 其他字段...
}

「核心属性」(常用):

  • value:数据库字段名(字段名一致时可省略);

  • exist:是否为数据库表字段(默认true,若实体字段不是表中字段,需设为false);

  • fill:字段自动填充策略(如创建时间、修改时间,后续详解);

  • select:是否参与查询(默认true,若不想查询该字段,设为false);

  • update:更新时的注入逻辑(如update="%s+1",表示更新时自增1)。

4. @TableLogic(逻辑删除注解)

「作用」:实现逻辑删除(数据不真正删除,仅标记删除状态),避免误删除数据无法恢复。

「使用场景」:实际开发中,几乎所有表都需要逻辑删除,替代物理删除。

「使用步骤」:

  1. 数据库表添加deleted字段(int类型,默认值0,0=未删除,1=已删除);

  2. 实体类添加deleted字段,并添加@TableLogic注解;

  3. 配置文件中配置逻辑删除的默认值。

// 实体类字段
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    // 逻辑删除字段
    @TableLogic
    private Integer deleted;
    // 其他字段...
}
# 配置逻辑删除(application.properties)
mybatis-plus:
  global-config:
    db-config:
      logic-delete-value: 1 # 逻辑已删除值(默认1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认0)

「逻辑删除原理」:

  • 删除操作:实际执行update语句,将deleted设为1(而非delete语句);

  • 查询操作:自动追加where deleted=0条件,过滤已删除数据;

  • 更新操作:自动追加where deleted=0条件,避免更新已删除数据。

5. 其他常用注解(了解即可)

  • @OrderBy:内置SQL默认排序,优先级低于条件构造器;

  • @Version:乐观锁注解,用于解决并发更新冲突;

  • @EnumValue:普通枚举类注解,用于枚举字段与数据库值的映射;

  • @KeySequence:序列主键策略(适用于Oracle数据库)。

五、Mapper CRUD接口(核心功能)

MP的BaseMapper接口内置了所有单表CRUD方法,无需手动编写SQL,直接调用即可。以下是最常用的方法,结合案例详细讲解。

「说明」:

  • 泛型T为实体类对象;

  • 参数Serializable为任意类型主键(推荐使用Long);

  • 参数Wrapper为条件构造器(后续详解),null表示无查询条件。

1. 插入操作(insert)

「方法格式」:int insert(T entity); // 插入一条记录,返回受影响行数

「案例」:插入一条用户数据,主键自动生成(雪花算法)

@Test
public void testInsert() {
    User user = new User();
    user.setName("李思思");
    user.setAge(18);
    user.setEmail("379872721@qq.com");
    // 插入数据,MP自动生成主键
    int rows = userMapper.insert(user);
    System.out.println("受影响行数:" + rows);
    System.out.println("插入的用户ID:" + user.getId()); // 主键自动回填
}

「注意」:主键生成策略可通过@TableId(type = ...)局部设置,或通过全局配置设置(优先级:局部 > 全局)。

2. 更新操作(update)

MP提供两种更新方式,最常用的是根据ID更新和根据条件更新。

方式1:根据ID更新(updateById)
@Test
public void testUpdateById() {
    User user = new User();
    user.setId(6L); // 必须设置主键ID
    user.setName("李思思_修改");
    user.setAge(19);
    // 根据ID更新,只更新非null字段
    int rows = userMapper.updateById(user);
    System.out.println("受影响行数:" + rows);
}
方式2:根据条件更新(update + Wrapper)
@Test
public void testUpdateByWrapper() {
    // 1. 创建条件构造器,设置更新条件
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("name", "李思思_修改"); // 条件:name等于李思思_修改
    // 2. 创建更新对象,设置更新内容
    User user = new User();
    user.setAge(20);
    // 3. 根据条件更新
    int rows = userMapper.update(user, wrapper);
    System.out.println("受影响行数:" + rows);
}

3. 查询操作(常用5种)

查询操作是开发中最常用的,MP提供了多种查询方式,覆盖大部分场景。

(1)根据ID查询(selectById)
@Test
public void testSelectById() {
    User user = userMapper.selectById(1L);
    System.out.println(user); // 输出查询到的用户信息
}
(2)根据ID批量查询(selectBatchIds)
@Test
public void testSelectBatchIds() {
    // 批量查询ID为1、2、3的用户
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
    users.forEach(System.out::println);
}
(3)根据多条件查询(selectByMap)

「说明」:Map中的key为数据库字段名,value为查询值,多条件为“且”关系。

@Test
public void testSelectByMap() {
    Map<String, Object> map = new HashMap<>();
    map.put("name", "Jack"); // 条件1:name=Jack
    map.put("age", 20); // 条件2:age=20
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}
(4)查询所有数据(selectList)
@Test
public void testSelectList() {
    // selectList(null) 表示无任何条件,查询所有
    List<User> users = userMapper.selectList(null);
    users.forEach(System.out::println);
}
(5)查询总记录数(selectCount)
@Test
public void testSelectCount() {
    // 查询所有记录数(无条件)
    Integer count = userMapper.selectCount(null);
    System.out.println("总记录数:" + count);
    
    // 带条件查询记录数(查询age>=18的用户数)
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.ge("age", 18);
    Integer count2 = userMapper.selectCount(wrapper);
    System.out.println("age>=18的用户数:" + count2);
}

4. 分页查询(核心,必掌握)

分页是网站开发中必备功能,MP内置分页插件,无需额外引入第三方插件(如PageHelper),配置简单、使用便捷。

实现步骤(2步)
步骤1:配置分页拦截器

创建配置类(如MybatisPlusConfig),注册分页拦截器,指定数据库类型(本文为MySQL):

@Configuration
public class MybatisPlusConfig {
    // 最新版分页插件配置
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分页拦截器,指定数据库类型为MySQL
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(com.baomidou.mybatisplus.core.toolkit.supportDbType.MYSQL));
        return interceptor;
    }
}
步骤2:编写分页查询代码
@Test
public void testSelectPage() {
    // 1. 创建分页对象,参数1:当前页码,参数2:每页显示条数
    Page<User> page = new Page<>(2, 3); // 第2页,每页3条数据
    // 2. 执行分页查询,null表示无查询条件
    Page<User> userPage = userMapper.selectPage(page, null);
    
    // 3. 获取分页结果
    List<User> records = userPage.getRecords(); // 分页查询的数据列表
    long total = userPage.getTotal(); // 总记录数
    long current = userPage.getCurrent(); // 当前页码
    long pages = userPage.getPages(); // 总页码
    boolean hasNext = userPage.hasNext(); // 是否有下一页
    boolean hasPrevious = userPage.hasPrevious(); // 是否有上一页
    
    // 输出结果
    System.out.println("分页数据:");
    records.forEach(System.out::println);
    System.out.println("总记录数:" + total);
    System.out.println("当前页码:" + current);
    System.out.println("总页码:" + pages);
}

5. 删除操作(物理删除 + 逻辑删除)

删除操作分为物理删除和逻辑删除,实际开发中优先使用逻辑删除。

(1)物理删除(直接删除数据)
// 1. 根据ID删除
@Test
public void testDeleteById() {
    int rows = userMapper.deleteById(6L);
    System.out.println("受影响行数:" + rows);
}

// 2. 根据ID批量删除
@Test
public void testDeleteBatchIds() {
    int rows = userMapper.deleteBatchIds(Arrays.asList(6L, 7L));
    System.out.println("受影响行数:" + rows);
}

// 3. 根据条件删除
@Test
public void testDeleteByMap() {
    Map<String, Object> map = new HashMap<>();
    map.put("name", "李思思");
    map.put("age", 18);
    int rows = userMapper.deleteByMap(map);
    System.out.println("受影响行数:" + rows);
}
(2)逻辑删除(推荐)

配置逻辑删除后,调用delete系列方法,实际执行的是update操作(将deleted设为1),具体案例参考「@TableLogic注解」部分。

六、条件构造器(Wrapper,核心难点)

当我们需要编写复杂的SQL查询条件(如多条件、模糊查询、排序、子查询等)时,就需要使用MP的条件构造器Wrapper。

MP提供两种常用的条件构造器:

  • QueryWrapper:普通条件构造器,使用字符串指定字段名(易出错);

  • LambdaQueryWrapper:Lambda条件构造器,通过Lambda表达式指定字段名(推荐,避免字段名写错)。

参考官网条件构造器文档:https://baomidou.com/pages/10c804/#abstractwrapper

常用条件构造方法(实战案例)

以下案例均使用QueryWrapper,LambdaQueryWrapper用法类似,只需将“字段名字符串”替换为Lambda表达式(如user -> user.getName())。

案例1:多条件查询(非空 + 范围)

需求:查询姓名不为空、邮箱不为空、年龄大于等于12的用户。

@Test
void testWrapper1() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.isNotNull("name") // 姓名不为空
           .isNotNull("email") // 邮箱不为空
           .ge("age", 12); // 年龄>=12
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}
案例2:精确查询(等于)

需求:查询姓名为“Jack”的用户(注意:selectOne只能查询一条记录,多一条会报错)。

@Test
void testWrapper2() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("name", "Jack"); // 姓名等于Jack
    User user = userMapper.selectOne(wrapper);
    System.out.println(user);
}
案例3:范围查询(between)

需求:查询年龄在20~30之间的用户数。

@Test
void testWrapper3() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.between("age", 20, 30); // 年龄在20到30之间(包含20和30)
    Integer count = userMapper.selectCount(wrapper);
    System.out.println("年龄在20~30之间的用户数:" + count);
}
案例4:模糊查询(like)

需求:查询姓名不包含“e”、邮箱以“t”开头的用户。

@Test
void testWrapper4() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.notLike("name", "e") // 姓名不包含e(%e%)
           .likeRight("email", "t"); // 邮箱以t开头(t%)
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}
案例5:子查询(inSql)

需求:查询ID大于等于2的用户(子查询:select id from user where id>=2)。

@Test
void testWrapper5() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.inSql("id", "select id from user where id >= 2"); // 子查询
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}
常用条件构造方法总结
  • eq:等于(=);ne:不等于(!=);

  • gt:大于(>);ge:大于等于(>=);lt:小于(&lt;);le:小于等于(&lt;=);

  • like:模糊查询(%值%);likeLeft:左模糊(%值);likeRight:右模糊(值%);

  • between:范围查询(between 左 and 右);

  • in:包含查询(in (值1, 值2));inSql:子查询包含;

  • isNull:字段为空;isNotNull:字段不为空;

  • orderByAsc:升序排序;orderByDesc:降序排序。

七、总结

MyBatis-Plus作为MyBatis的增强工具,核心价值在于“简化开发、提高效率”,它没有改变MyBatis的原生语法,却解决了MyBatis的诸多痛点。

本文从概述、快速入门、日志配置、常用注解、Mapper CRUD、条件构造器等核心模块,详细讲解了MyBatis-Plus的使用方法,覆盖了开发中90%以上的场景。掌握这些内容,你可以轻松应对单表CRUD、分页、复杂查询等需求,摆脱重复的SQL编写工作。

后续还可以深入学习MP的高级功能,如代码生成器、全局通用方法注入、乐观锁、字段自动填充等,进一步提升开发效率。

最后,附上MyBatis-Plus官网地址,建议大家多查阅官网文档,获取最准确的功能说明:https://baomidou.com/

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐