MyBatis-Plus:让数据库操作飞起来的神器

作者:小码哥_常日期:2026/4/28

MyBatis-Plus:让数据库操作飞起来的神器

一、MyBatis-Plus 是什么?

在 Java 开发的世界里,数据库操作是不可或缺的一部分。MyBatis 作为一款优秀的持久层框架,深受开发者喜爱,而 MyBatis-Plus 则是在 MyBatis 基础上诞生的强大增强工具。它就像是给 MyBatis 这位 “武林高手” 配备了一套超级装备,让开发变得更加高效和便捷。

MyBatis-Plus 秉持着 “只做增强不做改变” 的设计理念 ,这意味着当你引入它到项目中时,就像给原有的 MyBatis 系统轻轻地披上一层 “魔法披风”,不会对现有工程产生任何侵入性影响。它保留了 MyBatis 的所有优点,比如灵活的 SQL 自定义、强大的映射功能等,同时又添加了许多实用的新特性,让开发人员从繁琐的基础代码编写中解脱出来。

想象一下,在传统的数据库操作中,我们常常需要为每一个基本的增删改查(CRUD)操作编写大量重复的 SQL 语句,这不仅耗时费力,还容易出错。而 MyBatis-Plus 内置了通用的 Mapper 和 Service,通过少量的配置,就能轻松实现单表大部分的 CRUD 操作 。就好比有了一个万能的 “数据库操作助手”,只要告诉它你的需求,它就能快速准确地完成任务,大大提高了开发效率。

例如,在查询用户信息时,以往我们可能需要编写复杂的 SQL 语句来构建查询条件,而现在使用 MyBatis-Plus,借助它强大的条件构造器,只需通过简单的代码就能实现各种复杂查询,代码量大幅减少,而且可读性更强。 它还支持 Lambda 形式调用,让我们在编写查询条件时,无需再担心字段写错,就像有了一个智能的代码检查器,时刻为我们的代码质量保驾护航。

二、为什么选择 MyBatis-Plus?

(一)原生 MyBatis 的痛点

在 MyBatis-Plus 出现之前,原生 MyBatis 在开发中虽然有着灵活的 SQL 定制能力,但也暴露出不少让人头疼的问题 。

大量重复代码就是其中之一,在使用原生 MyBatis 时,每一个数据库表都需要开发者手动编写对应的 Mapper 接口和 XML 映射文件,实现增删改查(CRUD)操作。想象一下,一个项目中如果有十几个甚至几十个表,那光是这些基础的 CRUD 代码就得花费大量的时间和精力去编写,而且这些代码结构相似,只是针对不同表的字段和表名有所变化,这无疑是一种重复劳动,不仅降低开发效率,还增加了维护成本。

XML 文件臃肿也是原生 MyBatis 难以避免的问题。即使是非常简单的查询操作,也需要在 XML 文件中编写完整的 SQL 语句。随着项目的不断迭代和功能的增加,XML 文件会越来越大,内容也越来越复杂,这使得代码的可读性和可维护性大幅下降。修改一个小功能,可能就需要在冗长的 XML 文件中艰难地找到对应的 SQL 语句,稍有不慎还可能影响其他功能。

分页功能繁琐同样困扰着开发者,在原生 MyBatis 中实现分页,需要手动编写分页 SQL 语句,不同数据库的分页语法还存在差异,比如 MySQL 使用 LIMIT 关键字实现分页,而 Oracle 则需要使用 ROWNUM 伪列。这就意味着开发者需要针对不同数据库编写不同的分页代码,大大增加了开发难度和工作量。此外,还需要手动处理分页参数,如当前页码、每页显示条数等,容易出现参数传递错误或计算错误的情况。

还有就是条件查询麻烦,当进行动态条件查询时,原生 MyBatis 需要在代码中编写大量的 if - else 判断语句来拼接 SQL 条件。这不仅使得代码变得冗长复杂,可读性差,而且还容易出现 SQL 注入风险,对系统的安全性构成威胁。例如,当根据用户输入的条件进行查询时,如果没有对用户输入进行严格的过滤和转义,恶意用户可能会通过输入特殊字符来篡改 SQL 语句,获取或修改不应该访问的数据。

(二)MyBatis-Plus 优势

MyBatis-Plus 的出现,就像是为这些问题量身定制的解决方案,完美地解决了原生 MyBatis 的诸多痛点 。

它的无侵入性是一大亮点,正如前面所说,它秉持 “只做增强不做改变” 的理念,在引入到项目中时,不会对原有的 MyBatis 代码结构和配置产生任何影响,就像给项目轻轻地披上一层 “隐形披风”,既能享受它带来的强大功能,又无需担心兼容性问题 。

强大的 CRUD 操作让开发效率大幅提升。MyBatis-Plus 内置了通用的 Mapper 和 Service,只要让自定义的 Mapper 接口继承 BaseMapper,就可以自动拥有单表的大部分 CRUD 方法,无需再编写大量重复的 SQL 语句和 XML 映射文件。这就好比有了一个 “超级代码生成器”,一键就能生成常用的数据库操作代码,让开发者从繁琐的基础代码编写中解放出来,专注于业务逻辑的实现。

在条件查询方面,MyBatis-Plus 提供了强大的条件构造器,如 QueryWrapper 和 LambdaQueryWrapper 。使用它们,开发者可以通过简单的方法调用轻松构建各种复杂的查询条件,而无需编写 SQL 语句。LambdaQueryWrapper 更是支持 Lambda 表达式,通过方法引用获取实体类字段,避免了手动输入字段名可能出现的拼写错误,同时也让代码更加简洁、易读。比如,想要查询年龄大于 18 岁且姓名中包含 “张” 的用户,使用 LambdaQueryWrapper 只需短短几行代码就能实现:

1LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
2wrapper.gt(User::getAge, 18).like(User::getName, "张");
3List<User> userList = userMapper.selectList(wrapper);
4

MyBatis-Plus 的内置分页插件也十分实用,只需要简单配置,就能实现物理分页功能。它会自动在 SQL 语句中添加分页相关的关键字和参数,适配不同的数据库,开发者无需再手动编写分页 SQL 和处理分页参数,大大简化了分页操作的实现过程 。比如,要实现查询第 2 页,每页显示 10 条数据,代码如下:

1Page<User> page = new Page<>(2, 10);
2Page<User> result = userMapper.selectPage(page, null);
3

此外,MyBatis-Plus 还拥有丰富的插件体系,支持逻辑删除、自动填充、乐观锁、多数据源等功能。逻辑删除可以在不真正删除数据的情况下,将数据标记为已删除状态,避免数据误删,同时也方便数据的回溯和统计;自动填充功能可以在插入或更新数据时,自动填充一些公共字段,如创建时间、更新时间等,减少手动赋值的操作;乐观锁插件则通过版本号机制解决了并发更新时的数据一致性问题,确保在高并发场景下数据的准确性 。

三、快速上手 MyBatis-Plus

(一)环境搭建

以 Spring Boot 项目为例,搭建 MyBatis-Plus 开发环境的步骤并不复杂 。

首先创建一个 Spring Boot 项目,可以使用 IDEA 的 Spring Initializr 快速创建,也可以通过 Maven Archetype 手动创建。在创建过程中,记得勾选 Spring Web 依赖,这将为项目提供 Web 开发支持。

创建好项目后,在pom\.xml文件中添加 MyBatis-Plus 依赖。如果还需要连接数据库,比如 MySQL,还需要添加 MySQL 驱动依赖。如果想简化实体类的编写,使用 Lombok 也是个不错的选择,它能通过注解自动生成 getter、setter、equals、hashCode 和 toString 等方法 。以下是相关依赖配置示例:

1<dependencies>
2    <!-- Spring Boot 核心启动器 -->
3    <dependency>
4        <groupId>org.springframework.boot</groupId>
5        <artifactId>spring-boot-starter-web</artifactId>
6    </dependency>
7    <!-- MyBatis-Plus 启动器 -->
8    <dependency>
9        <groupId>com.baomidou</groupId>
10        <artifactId>mybatis-plus-boot-starter</artifactId>
11        <version>3.5.7</version>
12    </dependency>
13    <!-- MySQL 驱动 -->
14    <dependency>
15        <groupId>mysql</groupId>
16        <artifactId>mysql-connector-java</artifactId>
17        <scope>runtime</scope>
18    </dependency>
19    <!-- Lombok 简化实体类编写 -->
20    <dependency>
21        <groupId>org.projectlombok</groupId>
22        <artifactId>lombok</artifactId>
23        <optional>true</optional>
24    </dependency>
25</dependencies>
26

添加完依赖后,点击pom\.xml文件右上角的 Maven 图标刷新依赖,确保所有依赖都成功下载。

接着配置数据库连接,在src/main/resources目录下找到application\.yml文件(如果没有则新建一个),添加数据库连接相关配置 :

1spring:
2  datasource:
3    driver-class-name: com.mysql.cj.jdbc.Driver
4    url: jdbc:mysql://localhost:3306/your_database_name?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
5    username: your_username
6    password: your_password
7

记得将your\_database\_nameyour\_usernameyour\_password替换为实际的数据库名、用户名和密码 。

最后,为了让 Spring 能够扫描到 MyBatis-Plus 的 Mapper 接口,需要在 Spring Boot 启动类上添加@MapperScan注解,并指定 Mapper 接口所在的包路径 :

1import org.mybatis.spring.annotation.MapperScan;
2import org.springframework.boot.SpringApplication;
3import org.springframework.boot.autoconfigure.SpringBootApplication;
4
5@SpringBootApplication
6@MapperScan("com.example.demo.mapper") // 替换为实际的Mapper接口包路径
7public class DemoApplication {
8    public static void main(String[] args) {
9        SpringApplication.run(DemoApplication.class, args);
10    }
11}
12

(二)基本 CRUD 操作

完成环境搭建后,就可以使用 MyBatis-Plus 进行基本的 CRUD 操作了 。

假设我们有一个User实体类,对应数据库中的user表 :

1import com.baomidou.mybatisplus.annotation.TableName;
2import lombok.Data;
3
4@Data
5@TableName("user")
6public class User {
7    private Long id;
8    private String name;
9    private Integer age;
10    private String email;
11}
12

这里使用了 Lombok 的@Data注解简化代码,同时通过@TableName注解指定实体类对应的数据库表名。

接下来创建UserMapper接口,让它继承BaseMapper\&lt;User\&gt;,这样就自动拥有了单表的大部分 CRUD 方法 :

1import com.baomidou.mybatisplus.core.mapper.BaseMapper;
2import org.apache.ibatis.annotations.Mapper;
3
4@Mapper
5public interface UserMapper extends BaseMapper<User> {
6}
7

有了实体类和 Mapper 接口,就可以进行具体的 CRUD 操作了 。

插入操作:使用insert方法将一个User对象插入到数据库中 :

1import com.example.demo.entity.User;
2import com.example.demo.mapper.UserMapper;
3import org.junit.jupiter.api.Test;
4import org.springframework.beans.factory.annotation.Autowired;
5import org.springframework.boot.test.context.SpringBootTest;
6
7@SpringBootTest
8public class UserMapperTest {
9
10    @Autowired
11    private UserMapper userMapper;
12
13    @Test
14    public void testInsert() {
15        User user = new User();
16        user.setName("张三");
17        user.setAge(20);
18        user.setEmail("zhangsan@example.com");
19        int result = userMapper.insert(user);
20        System.out.println("插入成功,影响行数:" + result);
21        System.out.println("自动生成的ID:" + user.getId());
22    }
23}
24

运行测试方法后,会发现一条新的用户记录被插入到数据库中,并且user对象的id属性会自动填充为数据库生成的主键值,这得益于 MyBatis-Plus 默认的雪花算法策略生成 id 。

更新操作:使用updateById方法根据id更新User对象的信息 :

1@Test
2public void testUpdate() {
3    User user = new User();
4    user.setId(1L); // 假设要更新id为1的用户
5    user.setName("李四");
6    user.setAge(22);
7    int result = userMapper.updateById(user);
8    System.out.println("更新成功,影响行数:" + result);
9}
10

这里创建一个User对象,设置需要更新的id以及新的属性值,调用updateById方法即可完成更新操作,返回值为受影响的行数 。

删除操作:可以使用deleteById方法根据id删除用户记录 :

1@Test
2public void testDelete() {
3    Long id = 1L; // 假设要删除id为1的用户
4    int result = userMapper.deleteById(id);
5    System.out.println("删除成功,影响行数:" + result);
6}
7

调用deleteById方法并传入要删除的用户id,即可从数据库中删除对应的记录,返回值同样为受影响的行数 。

查询操作:使用selectById方法根据id查询用户信息 :

1@Test
2public void testSelect() {
3    Long id = 1L; // 假设要查询id为1的用户
4    User user = userMapper.selectById(id);
5    System.out.println("查询结果:" + user);
6}
7

调用selectById方法并传入id,即可从数据库中查询到对应的用户信息,并将结果封装成User对象返回 。

除了以上单个操作外,MyBatis-Plus 还提供了批量操作的方法,如deleteBatchIds用于批量删除,selectBatchIds用于批量查询等 ,使用方式与单个操作类似,只是传入的参数变为id的集合 。通过这些简单的方法调用,就能轻松完成数据库的基本 CRUD 操作,大大提高了开发效率 。

四、MyBatis-Plus 高级特性

(一)条件构造器(Wrapper)

在实际的数据库操作中,简单的 CRUD 往往无法满足复杂的业务需求,我们经常需要构建各种复杂的查询条件。MyBatis-Plus 的条件构造器(Wrapper)就像是一把 “万能钥匙”,为我们打开了复杂查询的大门。

其中,QueryWrapperUpdateWrapper是最常用的两个条件构造器 。QueryWrapper主要用于构建查询条件,而UpdateWrapper则用于构建更新条件。它们都支持链式调用,让我们可以像搭积木一样,轻松地组合出各种复杂的查询和更新逻辑 。

比如,我们要查询年龄大于 18 岁且姓名中包含 “张” 的用户,并且按照年龄降序排列 。使用QueryWrapper可以这样实现:

1QueryWrapper<User> wrapper = new QueryWrapper<>();
2wrapper.gt("age", 18)
3       .like("name", "张")
4       .orderByDesc("age");
5List<User> userList = userMapper.selectList(wrapper);
6

在这段代码中,gt方法表示 “大于”,like方法表示 “模糊查询”,orderByDesc方法表示 “降序排列” 。通过链式调用,我们可以清晰地表达出复杂的查询条件,而且代码简洁易读 。

再比如,我们要将年龄大于 30 岁的用户的邮箱统一更新为 “new\_email@example.com” 。使用UpdateWrapper可以这样实现:

1UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
2updateWrapper.set("email", "new_email@example.com")
3              .gt("age", 30);
4int result = userMapper.update(null, updateWrapper);
5

这里的set方法用于设置更新的字段和值,gt方法用于指定更新的条件 。通过UpdateWrapper,我们可以在不创建实体对象的情况下,直接设置更新字段和条件,实现高效的更新操作 。

除了QueryWrapperUpdateWrapper,MyBatis-Plus 还提供了基于 Lambda 表达式的LambdaQueryWrapperLambdaUpdateWrapper 。它们通过 Lambda 表达式引用实体类的属性,避免了硬编码字段名,进一步提高了代码的可读性和可维护性 。例如,上述查询年龄大于 18 岁且姓名中包含 “张” 的用户的代码,使用LambdaQueryWrapper可以写成:

1LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
2wrapper.gt(User::getAge, 18)
3       .like(User::getName, "张")
4       .orderByDesc(User::getAge);
5List<User> userList = userMapper.selectList(wrapper);
6

这样,即使实体类的字段名发生变化,只要属性名不变,代码就无需修改,大大降低了维护成本 。

(二)分页查询

在处理大量数据时,分页查询是必不可少的功能 。MyBatis-Plus 提供了便捷的分页插件,让分页查询变得轻而易举 。

要使用分页功能,首先需要配置分页插件 。在 Spring Boot 项目中,可以通过创建一个配置类来实现:

1import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
2import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
3import org.springframework.context.annotation.Bean;
4import org.springframework.context.annotation.Configuration;
5
6@Configuration
7public class MyBatisPlusConfig {
8    @Bean
9    public MybatisPlusInterceptor mybatisPlusInterceptor() {
10        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
11        // 添加分页插件,这里以MySQL数据库为例
12        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
13        return interceptor;
14    }
15}
16

在这个配置类中,我们创建了一个MybatisPlusInterceptor拦截器,并向其中添加了PaginationInnerInterceptor分页插件 。这样,MyBatis-Plus 就会自动在 SQL 语句中添加分页相关的逻辑 。

配置好分页插件后,就可以在代码中使用分页查询了 。例如,要查询第 2 页,每页显示 10 条用户数据 :

1import com.baomidou.mybatisplus.core.metadata.IPage;
2import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
3import org.junit.jupiter.api.Test;
4import org.springframework.beans.factory.annotation.Autowired;
5import org.springframework.boot.test.context.SpringBootTest;
6
7@SpringBootTest
8public class UserMapperTest {
9
10    @Autowired
11    private UserMapper userMapper;
12
13    @Test
14    public void testPage() {
15        // 构建分页对象,参数分别为当前页码和每页显示条数
16        Page<User> page = new Page<>(2, 10);
17        IPage<User> userPage = userMapper.selectPage(page, null);
18        // 获取当前页的数据列表
19        userPage.getRecords().forEach(System.out::println);
20        // 获取总记录数
21        System.out.println("总记录数:" + userPage.getTotal());
22        // 获取总页数
23        System.out.println("总页数:" + userPage.getPages());
24    }
25}
26

在这段代码中,我们创建了一个Page对象,传入当前页码和每页显示条数 。然后调用userMapper\.selectPage方法,传入Page对象和查询条件(这里为null,表示查询所有数据) 。selectPage方法会返回一个IPage对象,通过它我们可以获取当前页的数据列表、总记录数和总页数等信息 。

通过 MyBatis-Plus 的分页插件,我们无需手动编写分页 SQL 语句,也无需关心不同数据库的分页语法差异,就能轻松实现高效的分页查询功能 。

(三)逻辑删除

在实际应用中,有时我们并不希望真正删除数据库中的数据,而是将其标记为已删除状态,以便在需要时进行数据回溯和统计分析 。这就是逻辑删除的作用 。

逻辑删除是指在数据库表中添加一个逻辑删除字段(通常为一个布尔类型、整数类型或枚举类型的字段),用来表示数据是否被删除 。当执行删除操作时,实际上是将该字段的值更新为表示已删除的状态,而不是真正从数据库中删除数据 。

在 MyBatis-Plus 中配置和使用逻辑删除非常简单 。首先,在数据库表中添加逻辑删除字段,例如deleted字段,类型为int,默认值为0,表示未删除;值为1表示已删除 。

然后,在实体类中添加对应的逻辑删除属性,并使用@TableLogic注解标识 :

1import com.baomidou.mybatisplus.annotation.TableLogic;
2import lombok.Data;
3
4@Data
5public class User {
6    private Long id;
7    private String name;
8    private Integer age;
9    private String email;
10    // 逻辑删除字段
11    @TableLogic
12    private Integer deleted;
13}
14

最后,在配置文件中进行全局逻辑删除配置(也可以在@TableLogic注解中单独配置) :

1mybatis-plus:
2  global-config:
3    db-config:
4      logic-delete-field: deleted # 全局逻辑删除的实体字段名
5      logic-delete-value: 1 # 逻辑已删除值
6      logic-not-delete-value: 0 # 逻辑未删除值
7

完成上述配置后,MyBatis-Plus 会自动在查询和删除操作中处理逻辑删除 。例如,当执行userMapper\.deleteById\(id\)方法时,实际上执行的是更新操作,将deleted字段的值更新为1,而不是真正删除数据 。在查询时,MyBatis-Plus 会自动在 SQL 语句中添加deleted = 0的条件,只查询未被逻辑删除的数据 。

通过逻辑删除,我们可以有效地避免数据误删,同时满足业务对数据历史记录的需求 。

(四)自动填充

在数据库表设计中,通常会有一些公共字段,如创建时间、更新时间、创建人、更新人等 。每次插入或更新数据时,都需要手动为这些字段赋值,这不仅繁琐,还容易出错 。MyBatis-Plus 的自动填充功能可以很好地解决这个问题 。

自动填充功能允许我们在插入或更新数据时,自动为某些字段填充指定的值 。要使用自动填充功能,首先需要在实体类的字段上添加@TableField注解,并设置fill属性来指定填充时机 。例如,对于创建时间字段createTime,在插入数据时自动填充当前时间;对于更新时间字段updateTime,在更新数据时自动填充当前时间 :

1import com.baomidou.mybatisplus.annotation.TableField;
2import lombok.Data;
3
4import java.time.LocalDateTime;
5
6@Data
7public class User {
8    private Long id;
9    private String name;
10    private Integer age;
11    private String email;
12    // 创建时间,插入时自动填充
13    @TableField(fill = FieldFill.INSERT)
14    private LocalDateTime createTime;
15    // 更新时间,更新时自动填充
16    @TableField(fill = FieldFill.UPDATE)
17    private LocalDateTime updateTime;
18}
19

然后,创建一个实现MetaObjectHandler接口的类,来实现自动填充的逻辑 :

1import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
2import lombok.extern.slf4j.Slf4j;
3import org.apache.ibatis.reflection.MetaObject;
4import org.springframework.stereotype.Component;
5
6import java.time.LocalDateTime;
7
8@Component
9@Slf4j
10public class MyMetaObjectHandler implements MetaObjectHandler {
11
12    @Override
13    public void insertFill(MetaObject metaObject) {
14        log.info("开始插入填充...");
15        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
16        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
17    }
18
19    @Override
20    public void updateFill(MetaObject metaObject) {
21        log.info("开始更新填充...");
22        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
23    }
24}
25

在这个类中,insertFill方法用于在插入数据时填充字段,updateFill方法用于在更新数据时填充字段 。strictInsertFillstrictUpdateFill方法是 MyBatis-Plus 提供的严格填充方法,确保填充字段在实体类中存在,若不存在则抛出异常 。

通过自动填充功能,我们可以大大简化代码,提高开发效率,同时保证数据的一致性和准确性 。

(五)乐观锁

在高并发环境下,当多个线程同时对数据库中的数据进行更新操作时,可能会出现数据更新冲突的问题 。例如,线程 A 和线程 B 同时读取了数据库中的一条数据,然后线程 A 对数据进行了更新并提交,接着线程 B 也对数据进行了更新并提交,由于线程 B 在更新时并不知道线程 A 已经对数据进行了修改,所以线程 B 的更新操作会覆盖线程 A 的更新结果,导致数据不一致 。

乐观锁是一种解决并发更新问题的机制,它假设在大多数情况下,并发操作不会发生冲突,只有在提交更新时才检查数据是否被其他事务修改过 。如果数据没有被修改过,则更新操作成功;如果数据已经被修改过,则更新操作失败,需要重新读取数据并进行更新 。

在 MyBatis-Plus 中配置和使用乐观锁也很简单 。首先,在实体类中添加一个版本号字段,例如version字段,并使用@Version注解标识 :

1import com.baomidou.mybatisplus.annotation.Version;
2import lombok.Data;
3
4@Data
5public class User {
6    private Long id;
7    private String name;
8    private Integer age;
9    private String email;
10    // 乐观锁版本号字段
11    @Version
12    private Integer version;
13}
14

然后,在配置类中注册乐观锁插件 :

1import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
2import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
3import org.springframework.context.annotation.Bean;
4import org.springframework.context.annotation.Configuration;
5
6@Configuration
7public class MyBatisPlusConfig {
8    @Bean
9    public MybatisPlusInterceptor mybatisPlusInterceptor() {
10        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
11        // 添加乐观锁插件
12        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
13        return interceptor;
14    }
15}
16

完成上述配置后,MyBatis-Plus 会在更新数据时自动检查版本号 。例如,当执行userMapper\.updateById\(user\)方法时,MyBatis-Plus 会生成类似于以下的 SQL 语句:

1UPDATE user SET name = '新名字', age = 25, version = version + 1 WHERE id = 1 AND version = 1;
2

在这个 SQL 语句中,version = 1用于确保数据在更新时未被其他事务修改 。如果当前数据的version值不是1,则更新操作不会成功,返回值为0

通过乐观锁机制,我们可以有效地避免高并发环境下的数据更新冲突问题,确保数据的一致性和完整性 。

五、实战案例:用户管理系统

(一)需求分析

为了更直观地展示 MyBatis-Plus 在实际项目中的应用,我们以一个简单的用户管理系统为例进行实战。这个用户管理系统需要具备以下基本功能:

  • 用户注册:用户可以在系统中填写用户名、密码、邮箱等信息进行注册,系统会对用户输入的信息进行校验,确保信息的合法性和完整性,同时要检查用户名是否已被注册 。
  • 用户登录:用户输入已注册的用户名和密码进行登录验证,系统会在数据库中查询对应的用户信息,验证密码是否正确,如果验证通过则允许用户登录 。
  • 用户查询:支持根据用户 ID 查询单个用户的详细信息,也可以根据用户名进行模糊查询,获取符合条件的用户列表 。此外,还需要支持分页查询,以便在处理大量用户数据时,能够高效地展示数据 。
  • 用户修改:已登录的用户可以修改自己的个人信息,如密码、邮箱等。管理员用户则可以修改任意用户的信息,包括用户名、角色等 。在修改时,要确保数据的一致性和安全性,例如修改密码时需要进行密码强度校验 。
  • 用户删除:管理员用户有权限删除指定的用户。为了避免数据的误删,采用逻辑删除的方式,即只是将用户数据在数据库中标记为已删除,而不是真正从数据库中删除 。

(二)数据库设计

根据上述需求,我们设计一个名为user的用户表,其结构如下:

1CREATE TABLE `user` (
2  [`id`](https://xplanc.org/primers/document/zh/10.Bash/90.%E5%B8%AE%E5%8A%A9%E6%89%8B%E5%86%8C/EX.id.md) bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
3  `username` varchar(50) NOT NULL COMMENT '用户名',
4  `password` varchar(100) NOT NULL COMMENT '密码',
5  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
6  `phone` varchar(20) DEFAULT NULL COMMENT '手机号',
7  `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:0-禁用,1-正常',
8  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
9  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
10  `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除:0-未删除,1-已删除',
11  PRIMARY KEY ([`id`](https://xplanc.org/primers/document/zh/10.Bash/90.%E5%B8%AE%E5%8A%A9%E6%89%8B%E5%86%8C/EX.id.md)),
12  UNIQUE KEY `uk_username` (`username`)
13) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
14
  • id:用户的唯一标识,使用自增长的bigint类型,确保每个用户都有一个唯一的 ID 。
  • username:用户名,varchar类型,长度为 50,不能为空,并且设置了唯一索引,保证用户名的唯一性 。
  • password:用户密码,varchar类型,长度为 100,用于存储加密后的用户密码,保障用户信息安全 。
  • email:用户邮箱,varchar类型,长度为 100,可为空,用于找回密码、接收系统通知等 。
  • phone:用户手机号,varchar类型,长度为 20,可为空,用于短信验证、账号绑定等 。
  • status:用户状态,tinyint类型,0 表示禁用,1 表示正常,用于控制用户是否能够登录系统 。
  • create\_time:用户创建时间,datetime类型,默认值为当前时间,记录用户注册的时间 。
  • update\_time:用户信息更新时间,datetime类型,默认值为当前时间,并且在数据更新时自动更新为当前时间,用于记录用户信息的最后修改时间 。
  • deleted:逻辑删除字段,tinyint类型,0 表示未删除,1 表示已删除,通过这个字段实现逻辑删除功能 。

(三)代码实现

接下来,我们结合 MyBatis-Plus 的功能,逐步实现用户管理系统的各个功能模块 。

1. 实体类:首先创建User实体类,用于映射数据库中的user表 。使用 Lombok 的@Data注解简化代码,通过@TableName注解指定实体类对应的数据库表名,使用@TableId注解标识主键字段,并指定主键生成策略为自增 。同时,添加@TableField注解实现自动填充功能,使用@TableLogic注解实现逻辑删除功能 。

1import com.baomidou.mybatisplus.annotation.*;
2import lombok.Data;
3
4import java.time.LocalDateTime;
5
6@Data
7@TableName("user")
8public class User {
9    @TableId(type = IdType.AUTO)
10    private Long id;
11    private String username;
12    private String password;
13    private String email;
14    private String phone;
15    private Integer status;
16    @TableField(fill = FieldFill.INSERT)
17    private LocalDateTime createTime;
18    @TableField(fill = FieldFill.INSERT_UPDATE)
19    private LocalDateTime updateTime;
20    @TableLogic
21    private Integer deleted;
22}
23

2. Mapper 接口:创建UserMapper接口,继承BaseMapper\&lt;User\&gt;,这样就自动拥有了单表的大部分 CRUD 方法 。

1import com.baomidou.mybatisplus.core.mapper.BaseMapper;
2import org.apache.ibatis.annotations.Mapper;
3
4@Mapper
5public interface UserMapper extends BaseMapper<User> {
6}
7

3. Service 层:创建UserService接口及其实现类UserServiceImpl 。在UserService接口中定义用户管理的业务方法,如注册、登录、查询、修改、删除等 。在UserServiceImpl中实现这些方法,通过调用UserMapper来操作数据库 。

UserService接口:

1import com.baomidou.mybatisplus.extension.service.IService;
2import com.example.demo.entity.User;
3
4public interface UserService extends IService<User> {
5    // 用户注册
6    boolean register(User user);
7    // 用户登录
8    User login(String username, String password);
9    // 根据ID查询用户
10    User getUserById(Long id);
11    // 根据用户名模糊查询用户列表
12    List<User> listUsersByUsername(String username);
13    // 更新用户信息
14    boolean updateUser(User user);
15    // 删除用户(逻辑删除)
16    boolean deleteUser(Long id);
17}
18

UserServiceImpl实现类:

1import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
2import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
3import com.example.demo.entity.User;
4import com.example.demo.mapper.UserMapper;
5import com.example.demo.service.UserService;
6import org.springframework.stereotype.Service;
7import org.springframework.util.DigestUtils;
8
9import java.util.List;
10
11@Service
12public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
13
14    @Override
15    public boolean register(User user) {
16        // 检查用户名是否已存在
17        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
18        wrapper.eq(User::getUsername, user.getUsername());
19        if (count(wrapper) > 0) {
20            return false;
21        }
22        // 对密码进行加密
23        String encryptedPassword = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
24        user.setPassword(encryptedPassword);
25        // 插入用户数据
26        return save(user);
27    }
28
29    @Override
30    public User login(String username, String password) {
31        // 对密码进行加密
32        String encryptedPassword = DigestUtils.md5DigestAsHex(password.getBytes());
33        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
34        wrapper.eq(User::getUsername, username)
35               .eq(User::getPassword, encryptedPassword)
36               .eq(User::getDeleted, 0);
37        return getOne(wrapper);
38    }
39
40    @Override
41    public User getUserById(Long id) {
42        return getById(id);
43    }
44
45    @Override
46    public List<User> listUsersByUsername(String username) {
47        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
48        wrapper.like(User::getUsername, username)
49               .eq(User::getDeleted, 0);
50        return list(wrapper);
51    }
52
53    @Override
54    public boolean updateUser(User user) {
55        return updateById(user);
56    }
57
58    @Override
59    public boolean deleteUser(Long id) {
60        return removeById(id);
61    }
62}
63

4. Controller 层:创建UserController,用于处理 HTTP 请求,调用UserService的方法来实现用户管理的功能 。通过@RestController注解将该类标记为 RESTful 风格的控制器,使用@RequestMapping注解映射请求路径 。

1import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
2import com.example.demo.entity.User;
3import com.example.demo.service.UserService;
4import org.springframework.web.bind.annotation.*;
5
6import javax.annotation.Resource;
7import java.util.List;
8
9@RestController
10@RequestMapping("/user")
11public class UserController {
12
13    @Resource
14    private UserService userService;
15
16    // 用户注册
17    @PostMapping("/register")
18    public boolean register(@RequestBody User user) {
19        return userService.register(user);
20    }
21
22    // 用户登录
23    @PostMapping("/login")
24    public User login(@RequestParam String username, @RequestParam String password) {
25        return userService.login(username, password);
26    }
27
28    // 根据ID查询用户
29    @GetMapping("/{id}")
30    public User getUserById(@PathVariable Long id) {
31        return userService.getUserById(id);
32    }
33
34    // 根据用户名模糊查询用户列表
35    @GetMapping("/list")
36    public List<User> listUsersByUsername(@RequestParam String username) {
37        return userService.listUsersByUsername(username);
38    }
39
40    // 更新用户信息
41    @PutMapping
42    public boolean updateUser(@RequestBody User user) {
43        return userService.updateUser(user);
44    }
45
46    // 删除用户(逻辑删除)
47    @DeleteMapping("/{id}")
48    public boolean deleteUser(@PathVariable Long id) {
49        return userService.deleteUser(id);
50    }
51
52    // 分页查询用户列表
53    @GetMapping("/page")
54    public Page<User> pageUsers(@RequestParam(defaultValue = "1") Integer current,
55                                @RequestParam(defaultValue = "10") Integer size) {
56        Page<User> page = new Page<>(current, size);
57        return userService.page(page);
58    }
59}
60

通过以上代码,我们利用 MyBatis-Plus 的强大功能,实现了一个简单但功能完整的用户管理系统 。这个系统涵盖了用户注册、登录、查询、修改和删除等核心功能,并且充分展示了 MyBatis-Plus 在简化数据库操作方面的优势 。无论是基础的 CRUD 操作,还是复杂的条件查询和分页功能,MyBatis-Plus 都能轻松应对,让开发变得更加高效和便捷 。


MyBatis-Plus:让数据库操作飞起来的神器》 是转载文章,点击查看原文


相关推荐


开源 Wiki 神器 Docmost:团队协作知识库的终极解决方案
修己xj2026/4/20

在团队协作中,文档管理始终是一个让人头疼的问题。传统的文档工具要么功能单一,要么价格昂贵,要么数据不在自己手里。今天,我要向大家推荐一款开源的协作式 Wiki 软件 —— Docmost。 什么是Docmost? Docmost 是一款开源的协作式 Wiki 和文档管理软件,专为团队知识管理而设计。它提供了实时协作、权限管理、空间隔离等企业级功能,同时保持了开源软件的透明性和可控性。 github 地址: github.com/docmost/doc… 文档地址: docmost.com/do


Laravel vs ThinkPHP3.x:现代框架对决
普通网友2026/4/11

好的,我们来比较一下 Laravel 和 ThinkPHP 3.x 这两个 PHP 框架的主要特点和差异。请注意,ThinkPHP 3.x 是一个相对较老的版本(ThinkPHP 已发展至 5.x/6.x/8.x),而 Laravel 则代表了更现代的 PHP 开发框架。 核心架构与设计理念 Laravel 设计哲学: 遵循 SOLID 原则和 DRY(Don't Repeat Yourself)原则。强调优雅、简洁、表达力强的代码。架构: 采用了 MVC(Model-View-Co


如何使用edge浏览器打开控制台?
宇宙计算机2026/4/3

如何使用edge浏览器打开控制台? 我们在edge浏览器中,点击F12,打开检查页面。(或者在页面上,鼠标右键 -> 工具) 但是我们点开检查页面以后,有的时候我们的屏幕过小,上面没有中文,我们不知道哪一个才是控制台。 那看下图,下图这个图标就是控制台。 然后我们就能在控制台的右下角键入JavaScript代码了


Linux 基础指令与权限管理完全指南
凤年徐2026/3/26

Linux 基础指令与权限管理完全指南 前言 在开始Linux系统编程之前,掌握常用的Linux命令和权限管理是必不可少的一步。本文将从零开始,带你熟悉Linux环境下的基本操作,包括文件管理、目录操作、文本查看、权限设置等核心内容。无论你是刚接触Linux的新手,还是希望系统梳理知识的老手,这篇文章都能为你提供清晰的指引。 思考:为什么生产环境中很少使用Linux的图形界面?因为图形界面会占用大量系统资源,且远程管理时效率低下,命令行才是Linux的精髓。 一、环境准备与远程登录 1. 查


mcp学习笔记(三)-Mcp传输协议代码示例
Shawn_Shawn2026/3/18

一、三种传输方式详解 1. Stdio 传输 1.1 原理说明 Stdio(标准输入输出)传输是最简单的传输方式。MCP Client 将Server 作为 子进程 启动,通过进程的 stdin 和 stdout 进行双向通信。 关键规则: 每条JSON-RPC 消息以 换行符 \n 分隔 stdout 专用于协议消息,日志/调试信息必须输出到 stderr Client 与 Server 一对一绑定,生命周期同步 1.2 原理图(draw.io) 1.3 交互流程图(draw.io 时


奔图M6200-6500系列打印机拆解教程?
喝酒没肉不香2026/3/9

1.后盖 取下 1 处两个螺丝,撬开 2 的卡爪,取下后盖。 2.左盖 拆下后盖后,撬开 1 处两个卡爪,取下左盖。 3.右盖 拆下后盖后,撬开 1、2 处 6 个卡爪,取下右盖。 4.扫描台 4.1拆下左盖后,取下 1 处的螺丝,拔除 2 处的三根连接线。 4.2取下 1 处的卡环,拔除支撑杆固定销,取下扫描台。 5.引擎板 拆除右盖后,拔除 1 处,取下 2 处螺丝,取下引擎板。 7.高压电源板 7.1取下引擎板后,取下


OpenClaw安装技能的三种方式
可夫小子2026/3/1

OpenClaw安装技能的三种方式 💡 大家好,我是可夫小子,《小白玩转ChatGPT》专栏作者,关注AI编程、AI自动化和自媒体。 OpenClaw很强大,但也需要搭配很多技能才能发挥最大的作用,它的安装方式没有像Claude Code那样复杂,但也还有一些操作过程。在OpenClaw里面,可以通过WebUI的界面,很直观的管理安装的技能。 但如果安装技能呢?今天分享常用的三种方式。 通过官方openclaw命令来配置 通过openclaw config 命令,选择Local→Ski


基于 YOLOv8 的水体污染目标检测系统 [目标检测完整源码]
我是杰尼2026/2/21

基于 YOLOv8 的水体污染目标检测系统 [目标检测完整源码] 一、背景:水体监控为什么需要“计算机视觉”? 在水资源保护与环境治理领域,“看得见问题”往往是治理的第一步。然而在真实场景中,水体监控长期面临以下现实挑战: 水域面积大、人工巡检成本高 污染物种类多、形态变化大 人工判读主观性强、难以量化 传统传感器难以识别“视觉型污染” 随着无人机、高清摄像头、遥感设备的普及,水体数据获取已不再是瓶颈,真正的难点转移到了如何从海量影像中自动识别污染风险。 基于此,本文介绍一套 以 YOLOv


【C++】多态
yuuki2332332026/2/12

前言: 在面向对象编程的学习脉络中,继承机制让代码的复用和层级设计成为可能,但仅靠继承无法完全体现对象行为的灵活性。比如我们通过继承定义了Person基类,以及Student、Soldier等派生类后,若想让不同对象执行 “买票” 这一相同名称的行为时展现出不同逻辑(普通人全价、学生打折、军人优先),单纯的继承语法无法高效实现这种 “一个接口,多种实现” 的需求。而多态作为继承的延伸与升华,恰好解决了这一问题 —— 它让继承体系下的不同对象,对同一行为能做出符合自身特性的响应,是面向对象编程中实


上下文工程(Context Engineering)
uzong2026/2/4

本文整理自 LangChain 博客《Context Engineering for Agents》」更自然 blog.langchain.com/context-eng… 随着大语言模型(LLM)在智能体(Agent)系统中的广泛应用,如何有效管理有限的上下文窗口(Context Window)成为关键挑战。 1. 为什么需要上下文工程? 大语言模型的上下文窗口类似于计算机的 RAM——它是模型的“工作记忆”,但容量有限。当智能体执行长期任务时,会不断累积以下类型的上下文: ● 指令(Inst

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客