跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
Javajava

Spring Boot 4 集成 MyBatis-Plus 实战指南

综述由AI生成如何在 Spring Boot 4 中集成 MyBatis-Plus,包括依赖配置、拦截器设置及基础特性。详细讲解了零 SQL CRUD、条件构造器、常用注解等核心功能,以及自动填充、逻辑删除和多数据源支持等增强能力。同时深入分析了分页、乐观锁、多租户等插件机制的执行流程,提供了自定义 SQL 的方法。最后对比了 MyBatis 一级与二级缓存的区别,阐述了 Mapper 代理及 Spring Boot 启动时的初始化加载流程,帮助开发者全面掌握 MyBatis-Plus 的使用与底层原理。

狂少发布于 2026/3/21更新于 2026/4/2621 浏览
Spring Boot 4 集成 MyBatis-Plus 实战指南

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

官方地址: Git 源码 文档

Spring Boot 4 如何集成

增加依赖

添加 MyBatis-Plus 依赖:

<properties>
    <mybatisplus.version>3.5.15</mybatisplus.version>
</properties>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot4-starter</artifactId>
    <version>${mybatisplus.version}</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-jsqlparser</artifactId>
    <version>${mybatisplus.version}</version>
</dependency>

配置

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        // 数据权限
        mybatisPlusInterceptor.addInnerInterceptor(new DataFilterInterceptor());
        // 分页插件
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        // 乐观锁
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        // 防止全表更新与删除
        mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

基础特性

  1. 零 SQL CRUD
    • 核心点:业务模块的 Mapper 接口继承 BaseMapper<T>,即可拥有 insert、selectById、updateById、deleteBatchIds 等 17 个常用方法。
    • Service CRUD:除了 Mapper 层,MP 还提供了 IService 和 ServiceImpl,封装了更多的业务逻辑方法(如 saveBatch 批量插入)。
    • 提供 SqlHelper 工具类实现批量写入或更新操作。
  2. 条件构造器(Wrapper)
    • 三板斧:QueryWrapper(用于查询,支持 lambda 避免字段名写错)、UpdateWrapper(用于更新,支持链式设置 set 值)。
    • Lambda 表达式:推荐使用 LambdaQueryWrapper,利用方法引用(如 User::getName)来指定字段,编译期检查,防止 SQL 拼写错误。
  3. 常用注解
    • @TableName:指定表名(如果类名与表名不一致)。
    • @TableId:指定主键策略(如 IdType.AUTO 自增,IdType.ASSIGN_ID 雪花算法)。
    • @TableField:填充字段(如创建时间、更新时间自动填充 FieldFill.INSERT_UPDATE),或者逻辑删除字段 @TableLogic。

增强功能

  1. 自动填充
    • 实现 MetaObjectHandler 接口,重写 insertFill 和 updateFill 方法,统一处理 create_time、update_time 等字段。
  2. 逻辑删除
    • 配置后,delete 语句会自动转化为 UPDATE 语句(设置删除标记),查询时自动追加 WHERE deleted = 0 条件。
    • 在实体类中,对应数据库表的逻辑删除字段上添加 @TableLogic 注解。
    • 插入:逻辑删除字段的值不受限制。
    • 查找:自动添加条件,过滤掉标记为已删除的记录。
    • 更新:防止更新已删除的记录。
    • 删除:将删除操作转换为更新操作,标记记录为已删除。
  3. 自定义 ID 生成器
    • IdentifierGenerator 主要用于生成数据库表的主键 ID。
    • KeyGenerator 是 MyBatis 框架中的一个接口,用于在执行 SQL 语句时生成键值,通常用于生成自增主键或者在执行 INSERT 语句后获取新生成的 ID。
    • MyBatis-Plus 内置支持多种数据库的主键生成策略,如:H2KeyGenerator, OracleKeyGenerator, PostgreKeyGenerator。
  4. 多数据源支持
    • Spring Boot 1.5.x ~ 2.x.x 使用 dynamic-datasource-spring-boot-starter,支持 JDK 8 及以上版本。
    • Spring Boot 3.x.x 使用 dynamic-datasource-spring-boot3-starter,要求 JDK 17 及以上。
    • Spring Boot 4.x.x 使用 dynamic-datasource-spring-boot4-starter,要求 JDK 17 及以上。

插件机制

常用插件

  1. 分页插件(PaginationInnerInterceptor)
    • 原理:MP 的分页不是内存分页,而是物理分页。它通过 MyBatis 的拦截器(Interceptor)机制,在 SQL 执行前重写 SQL(如加上 LIMIT)。
    • 使用:配置 MybatisPlusInterceptor,注入 PaginationInnerInterceptor。代码中使用 Page<T> 对象接收结果。
  2. 乐观锁插件(OptimisticLockerInnerInterceptor)
    1. 读取记录时,获取当前的版本号(version)。
    2. 在更新记录时,将这个版本号一同传递。
    3. 执行更新操作时,设置 version = newVersion 的条件为 version = oldVersion。
    4. 如果版本号不匹配,则更新失败。
  3. 多租户插件(TenantLineInnerInterceptor)
    1. 是 MyBatis-Plus 提供的一个插件,用于实现多租户的数据隔离。通过这个插件,可以确保每个租户只能访问自己的数据,从而实现数据的安全隔离。
    2. 默认插入 SQL 是需要判断租户条件,因此需要配合自动填充字段功能填充租户字段,否则租户字段不会自动保存到数据库。
  4. 非法 SQL 拦截插件(IllegalSQLInnerInterceptor)
    • 用于拦截和检查非法 SQL 语句。该插件旨在帮助开发者在 SQL 执行前发现并解决潜在的安全问题,如全表更新、删除操作,以及对索引的检查等。
  5. 防全表更新与删除插件(BlockAttackInnerInterceptor)
    1. 专门用于防止恶意的全表更新和删除操作。该插件通过拦截 update 和 delete 语句,确保这些操作不会无意中影响到整个数据表,从而保护数据的完整性和安全性。

执行流程

MyBatis-Plus(MP)插件的执行流程,本质上是基于 MyBatis 的插件(Plugin)机制实现的。MP 利用这一机制,在 MyBatis 的核心执行流程中'插入'自己的逻辑,从而实现分页、性能分析、SQL 注入防护等功能。

MybatisPlusInterceptor 是'包工头',它实现了 MyBatis 的接口并拦下所有活;而 List<InnerInterceptor>是'工人',实现具体的功能

组件层级类/接口名称职责实现关系
顶层门面MybatisPlusInterceptor实现 MyBatis 原生 Interceptor,作为唯一入口,管理插件列表。实现 org.apache.ibatis.plugin.Interceptor
插件容器List<InnerInterceptor>存储具体的增强逻辑(如分页、乐观锁),由顶层门面调用。内部持有
具体逻辑PaginationInnerInterceptor 等实现具体的业务逻辑(如重写 SQL、计算耗时)。实现 InnerInterceptor

如何自定义 SQL

通过如下注解,可以实现自定义 SQL:

  • @Select: 查询
  • @Insert: 插入
  • @Update: 更新
  • @Delete: 删除
@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 简单查询
    @Select("SELECT * FROM user WHERE email = #{email}")
    User selectByEmail(@Param("email") String email);

    // 复杂一点的条件查询
    @Select("SELECT * FROM user WHERE status = #{status} AND age > #{minAge}")
    List<User> selectByStatusAndAge(@Param("status") Integer status, @Param("minAge") Integer minAge);
}

MyBatis 知识

# {} 和 ${} 的区别

  • #{}:是预编译处理(PreparedStatement)。MyBatis 会将它替换为 ?,能有效防止 SQL 注入。
  • ${}:是字符串替换。直接将变量拼接到 SQL 中,存在注入风险,通常用于传入数据库表名或排序字段(ORDER BY)。

MyBatis 的一级缓存和二级缓存

  • 一级缓存: 作用域为 SqlSession。在同一个会话中,相同的查询不再查数据库。默认开启。
  • 二级缓存: 作用域为 Namespace(多个会话间共享)。需要手动开启,且实体类必须实现序列化接口。
  • 失效场景: 任何的 INSERT, UPDATE, DELETE 操作都会刷新缓存。

实体类属性名和表中字段名不一致怎么办?

  1. 在 SQL 语句中使用别名(AS)。
  2. 使用 ResultMap 进行映射(最常用)。
  3. 开启驼峰命名自动转换配置(mapUnderscoreToCamelCase)。

MyBatis 的缓存机制

一级缓存建议保持默认(开启),而二级缓存通常不建议开启(默认也是关闭的),尤其是在复杂的业务或分布式系统中

一级缓存

一级缓存的实现参见:BaseExecutor、PerpetualCache

  • 一级缓存的生命周期由 Executor(执行器)管理。在 BaseExecutor(所有执行器的基类)中,定义了本地缓存对象。
  • SqlSession 每次创建都会生成一个新的 Executor,所以一级缓存是线程隔离的【参见 DefaultSqlSessionFactory】。
二级缓存

二级缓存的实现参见:CachingExecutor、SynchronizedCache
当开启二级缓存后,MyBatis 会使用 CachingExecutor 包装原来的 Executor

装饰器模式: 二级缓存的实现非常优雅,PerpetualCache 是基础,通过 SynchronizedCache(同步)、LoggingCache(日志)、ScheduledCache(定时)、SerializedCache(序列化)、LruCache(回收策略)等装饰器层层包装,组合出最终需要的功能

特性一级缓存 (Local)二级缓存 (Global)
作用范围单个 SqlSession 内同一个 Mapper 命名空间内
数据共享独享,不跨会话共享,跨 SqlSession
默认状态开启关闭 (需手动配置)
生命周期伴随 SqlSession 的创建与关闭伴随 SqlSessionFactory 的生命周期
清空时机增删改操作、close()、commit()对应 Mapper 的增删改操作、commit()
是否开启
缓存类型建议配置理由适用场景
一级缓存开启 (默认)安全、自动管理、提升单次会话性能所有场景(无需干预)
二级缓存关闭 (默认)避免脏读、避免分布式不一致复杂业务、分布式系统
二级缓存开启 (需评估)需处理好序列化和跨表失效问题单机应用、数据字典、纯读场景

一句话建议: 用好一级缓存,忘掉二级缓存,用 Redis 来做全局缓存

加载流程

MapperImpl 方法调用过程
  1. 代理拦截(MybatisMapperProxy)
    • MybatisMapperProxy 是在 Spring 容器进行依赖注入(@Autowired)时,由 MapperProxyFactory 调用 getObject 方法实例化的。
  2. 命令执行(MapperMethod):
    • MybatisMapperProxy 会拿到一个 MapperMethod 实例。
    • 调用 mapperMethod.execute(sqlSession, args)。
    • 此时,控制权从'代理层'转移到了'命令层'。
  3. SQL 执行(SqlSession/Executor):
    • MapperMethod 内部会根据 SQL 类型,调用 sqlSession.selectOne() 或 insert() 等方法。
    • 最终由 Executor 去 JDBC 层面执行 SQL。
public class MapperRegistry {
    private final Configuration config;
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new ConcurrentHashMap<>();

    public MapperRegistry(Configuration config) {
        this.config = config;
    }

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory) this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return (T) mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception e) {
                throw new BindingException("Error getting mapper instance. Cause: " + e, e);
            }
        }
    }
}
Spring Boot 启动时初始化过程
  1. SqlSessionFactory 初始化
    • MybatisAutoConfiguration -> MybatisSqlSessionFactoryBean -> SqlSessionFactory
  2. Mapper 注册与扫描
    • AutoConfiguredMapperScannerRegistrar -> MapperScannerConfigurer -> ClassPathMapperScanner -> ClassPathBeanDefinitionScanner -> MapperFactoryBean
  3. MybatisMapperProxy 实例化
    1. 获取 Mapper Bean:
      • 当 Spring 容器尝试填充 @Autowired private UserMapper userMapper; 时。
      • 容器发现这是一个 FactoryBean,于是调用 MapperFactoryBean.getObject()。
    2. 获取 SqlSession:
      • MapperFactoryBean.getObject() 内部会调用 this.getSqlSession().getMapper(this.mapperInterface);。
    3. 最终实例化 (核心点):
      • sqlSession.getMapper() 会委托给 (T)this.getConfiguration().getMapper(type, this);。
      • Configuration 持有一个 this.mapperRegistry.getMapper(type, sqlSession)(注册中心)。
      • MapperRegistry 里存着 Class -> MapperProxyFactory 的映射
public class MapperRegistry {
    private final Configuration config;
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new ConcurrentHashMap<>();

    public MapperRegistry(Configuration config) {
        this.config = config;
    }

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory) this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return (T) mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception e) {
                throw new BindingException("Error getting mapper instance. Cause: " + e, e);
            }
        }
    }
}

目录

  1. Spring Boot 4 如何集成
  2. 增加依赖
  3. 配置
  4. 基础特性
  5. 增强功能
  6. 插件机制
  7. 常用插件
  8. 执行流程
  9. 如何自定义 SQL
  10. MyBatis 知识
  11. # {} 和 ${} 的区别
  12. MyBatis 的一级缓存和二级缓存
  13. 实体类属性名和表中字段名不一致怎么办?
  14. MyBatis 的缓存机制
  15. 一级缓存
  16. 二级缓存
  17. 是否开启
  18. 加载流程
  19. MapperImpl 方法调用过程
  20. Spring Boot 启动时初始化过程
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Python+ROS2 通用智能系统工业级通信框架
  • Web 服务架构与 I/O 模型详解
  • 医疗 AI Agent 可信构建:不确定性治理与贝叶斯增强
  • Cursor、Windsurf、Kiro、Zed 与 VS Code 的 AI 编程工具定价对比
  • IntelliJ IDEA 接入 AI 编程助手:Copilot、DeepSeek 及 GPT-4o Mini
  • 机器人通讯总线选型:CAN/FD、高速 485 与 EtherCAT 深度对比
  • Git 原理与进阶使用:远程协作、分支管理及企业实践
  • 基于 LangChain 与 Gradio 构建个人知识助理
  • 本地 AI 绘画:麦橘超然 Flux 控制台部署与使用
  • Fast-GitHub GitHub 加速插件使用指南
  • 数据结构实战:链表经典面试题解析
  • Win10 升级后频繁弹出 Copilot 窗口?彻底禁用与关闭指南
  • Python 调用同花顺问财 API 获取金融数据实战指南
  • OpenClaw Web UI 无法访问问题排查与解决
  • JavaAI 辅助构建 SpringBoot 项目的实战体验
  • 4 位全加器设计与七段数码管显示实现
  • Ubuntu 24.04 LTS ufw 防火墙完整配置指南
  • 基于 FastGPT 与 MCP 协议构建工具增强型智能体
  • 从零搭建双模式可视化编程平台:Python 与 ROS2 集成实践
  • Gemini 辅助影视分镜头脚本自动生成实战指南

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online