二百斤的牛 發表於 2026-1-10 10:10:36

Spring boot 4 搞懂MyBatis-Plus的用法解析

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>Spring boot 4如何集成</li><ul class="second_class_ul"><li>增加依赖</li><li>配置</li></ul><li>基础特性</li><ul class="second_class_ul"></ul><li>增强功能</li><ul class="second_class_ul"></ul><li>插件机制</li><ul class="second_class_ul"><li>常用插件</li><li>执行流程</li></ul><li>如何自定义SQL</li><ul class="second_class_ul"></ul><li>mybatis知识</li><ul class="second_class_ul"><li># {}和${}的区别</li><li>MyBatis 的一级缓存和二级缓存</li><li>实体类属性名和表中字段名不一致怎么办?</li><li>Mybatis的缓存机制</li><ul class="third_class_ul"><li>一级缓存</li><li>二级缓存</li><li>是否开启</li></ul><li>加载流程</li><ul class="third_class_ul"><li>MapperImpl方法调用过程</li><li>Spring boot启动时初始化过程</li></ul></ul></ul></div><p>MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生</p>
<p>官方地址:<br />git源码<br />文档</p>
<p class="maodian"></p><h2>Spring boot 4如何集成</h2>
<p class="maodian"></p><h3>增加依赖</h3>
<p>Add MyBatis-Plus dependency</p>
<div class="jb51code"><pre class="brush:java;">&lt;mybatisplus.version&gt;3.5.15&lt;/mybatisplus.version&gt;
                &lt;dependency&gt;
            &lt;groupId&gt;com.baomidou&lt;/groupId&gt;
            &lt;artifactId&gt;mybatis-plus-spring-boot4-starter&lt;/artifactId&gt;
            &lt;version&gt;${mybatisplus.version}&lt;/version&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
            &lt;groupId&gt;com.baomidou&lt;/groupId&gt;
            &lt;artifactId&gt;mybatis-plus-jsqlparser&lt;/artifactId&gt;
            &lt;version&gt;${mybatisplus.version}&lt;/version&gt;
      &lt;/dependency&gt;</pre></div>
<p class="maodian"></p><h3>配置</h3>
<div class="jb51code"><pre class="brush:java;">@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;
    }
}</pre></div>
<p class="maodian"></p><h2>基础特性</h2>
<ol><li><strong>&ldquo;零SQL&rdquo; CRUD</strong>
<ul><li><strong>核心点</strong>:业务模块的Mapper 接口继承 <code>BaseMapper&lt;T&gt;</code>,即可拥有 <code>insert</code>、<code>selectById</code>、<code>updateById</code>、<code>deleteBatchIds</code> 等 17 个常用方法。</li><li><strong>Service CRUD</strong>:除了 Mapper 层,MP 还提供了 <code>IService</code> 和 <code>ServiceImpl</code>,封装了更多的业务逻辑方法(如 <code>saveBatch</code> 批量插入)。</li><li>提供SqlHelper工具类实现批量写入或更新操作</li></ul></li><li><strong>条件构造器(Wrapper)</strong><ul><li><strong>三板斧</strong>:<code>QueryWrapper</code>(用于查询,支持lambda避免字段名写错)、<code>UpdateWrapper</code>(用于更新,支持链式设置 set 值)。</li><li><strong>Lambda 表达式</strong>:推荐使用 <code>LambdaQueryWrapper</code>,利用方法引用(如 <code>User::getName</code>)来指定字段,编译期检查,防止 SQL 拼写错误。</li></ul></li><li><strong>常用注解</strong><ul><li><code>@TableName</code>:指定表名(如果类名与表名不一致)。</li><li><code>@TableId</code>:指定主键策略(如 <code>IdType.AUTO</code> 自增,<code>IdType.ASSIGN_ID</code> 雪花算法)。</li><li><code>@TableField</code>:填充字段(如创建时间、更新时间自动填充 <code>FieldFill.INSERT_UPDATE</code>),或者逻辑删除字段 <code>@TableLogic</code></li></ul></li></ol>
<p class="maodian"></p><h2>增强功能</h2>
<ol><li><strong>自动填充</strong>
<ul><li><strong>自动填充</strong>:实现 <code>MetaObjectHandler</code> 接口,重写 <code>insertFill</code> 和 <code>updateFill</code> 方法,统一处理 <code>create_time</code>、<code>update_time</code> 等字段。</li></ul></li></ol>
<ul><li><strong>逻辑删除</strong>:配置后,<code>delete</code> 语句会自动转化为 <code>UPDATE</code> 语句(设置删除标记),查询时自动追加 <code>WHERE deleted = 0</code> 条件。 【在实体类中,对应数据库表的逻辑删除字段上添加 <code>@TableLogic</code> 注解】
<ul><li><strong>插入</strong>:逻辑删除字段的值不受限制。</li><li><strong>查找</strong>:自动添加条件,过滤掉标记为已删除的记录。</li><li><strong>更新</strong>:防止更新已删除的记录。</li><li><strong>删除</strong>:将删除操作转换为更新操作,标记记录为已删除</li></ul></li><li><strong>自定义ID生成器</strong><ul><li><code>IdentifierGenerator</code>主要用于生成数据库表的主键ID</li><li><code>KeyGenerator</code>是MyBatis框架中的一个接口,用于在执行SQL语句时生成键值,通常用于生成自增主键或者在执行INSERT语句后获取新生成的ID</li><li>MyBatis-Plus 内置支持多种数据库的<strong>主键生成策略</strong>,如:<ul><li>H2KeyGenerator</li><li>OracleKeyGenerator</li><li>PostgreKeyGenerator</li></ul></li></ul></li><li><strong>多数据源支持</strong>:详细使用参见https://github.com/baomidou/dynamic-datasource<ul><li>Spring Boot 1.5.x ~ 2.x.x 使用 <code>dynamic-datasource-spring-boot-starter</code>,支持 JDK 8 及以上版本</li><li>Spring Boot 3.x.x 使用 <code>dynamic-datasource-spring-boot3-starter</code>,<strong>要求 JDK 17 及以上</strong></li><li>Spring Boot 4.x.x 使用 <code>dynamic-datasource-spring-boot4-starter</code>,<strong>要求 JDK 17 及以上</strong></li></ul></li></ul>
<p class="maodian"></p><h2>插件机制</h2>
<p class="maodian"></p><h3>常用插件</h3>
<ol><li><strong>分页插件(PaginationInnerInterceptor)</strong>
<ul><li><strong>原理</strong>:MP 的分页不是内存分页,而是物理分页。它通过 MyBatis 的拦截器(Interceptor)机制,在 SQL 执行前重写 SQL(如加上 <code>LIMIT</code>)。</li><li><strong>使用</strong>:配置 <code>MybatisPlusInterceptor</code>,注入 <code>PaginationInnerInterceptor</code>。代码中使用 <code>Page&lt;T&gt;</code> 对象接收结果。</li></ul></li><li><strong>乐观锁插件</strong><code>OptimisticLockerInnerInterceptor</code><ol><li>读取记录时,获取当前的版本号(version)。</li><li>在更新记录时,将这个版本号一同传递。</li><li>执行更新操作时,设置 <code>version = newVersion</code> 的条件为 <code>version = oldVersion</code>。</li><li>如果版本号不匹配,则更新失败。</li></ol></li><li><strong>多租户插件</strong> <code>TenantLineInnerInterceptor</code><ol><li>是 MyBatis-Plus 提供的一个插件,用于实现多租户的数据隔离。通过这个插件,可以确保每个租户只能访问自己的数据,从而实现数据的安全隔离</li><li>默认插入 SQL 是需要判断租户条件,因此需要配合自动填充字段功能填充租户字段,否则租户字段不会自动保存到数据库</li></ol></li><li><strong>非法SQL拦截插件</strong><code>IllegalSQLInnerInterceptor</code><ul><li>用于拦截和检查非法SQL语句。该插件旨在帮助开发者在SQL执行前发现并解决潜在的安全问题,如全表更新、删除操作,以及对索引的检查等</li></ul></li><li><strong>防全表更新与删除插件</strong><code>BlockAttackInnerInterceptor</code><ol><li>专门用于防止恶意的全表更新和删除操作。该插件通过拦截 <code>update</code> 和 <code>delete</code> 语句,确保这些操作不会无意中影响到整个数据表,从而保护数据的完整性和安全性</li></ol></li></ol>
<p class="maodian"></p><h3>执行流程</h3>
<p>MyBatis-Plus(MP)插件的执行流程,本质上是基于 <strong>MyBatis 的插件(Plugin)机制</strong>实现的。MP 利用这一机制,在 MyBatis 的核心执行流程中&ldquo;插入&rdquo;自己的逻辑,从而实现分页、性能分析、SQL 注入防护等功能。</p>
<p><code>MybatisPlusInterceptor</code> 是&ldquo;包工头&rdquo;,它实现了 MyBatis 的接口并拦下所有活;而 <code>List&lt;InnerInterceptor&gt;</code>是&ldquo;工人&rdquo;,实现具体的功能</p>
<table><thead><tr><th>组件层级</th><th>类/接口名称</th><th>职责</th><th>实现关系</th></tr></thead><tbody><tr><td><strong>顶层门面</strong></td><td><code>MybatisPlusInterceptor</code></td><td>实现 MyBatis 原生 <code>Interceptor</code>,作为唯一入口,管理插件列表。</td><td><strong>实现</strong><br /><code>org.apache.ibatis.</code><br /><code>plugin.Interceptor</code></td></tr><tr><td><strong>插件容器</strong></td><td><code>List&lt;InnerInterceptor&gt;</code></td><td>存储具体的增强逻辑(如分页、乐观锁),由顶层门面调用。</td><td>内部持有</td></tr><tr><td><strong>具体逻辑</strong></td><td><code>PaginationInnerInterceptor</code><br />等</td><td>实现具体的业务逻辑(如重写 SQL、计算耗时)。</td><td><strong>实现</strong><br /><code>InnerInterceptor</code></td></tr></tbody></table>
<p class="maodian"></p><h2>如何自定义SQL</h2>
<p>通过如下注解,可以实现自定义SQL</p>
<ul><li><code>@Select</code>: 查询</li><li><code>@Insert</code>: 插入</li><li><code>@Update</code>: 更新</li><li><code>@Delete</code>: 删除</li></ul>
<div class="jb51code"><pre class="brush:java;">@Mapper
public interface UserMapper extends BaseMapper&lt;User&gt; {
    // 简单查询
    @Select("SELECT * FROM user WHERE email = #{email}")
    User selectByEmail(@Param("email") String email);
    // 复杂一点的条件查询
    @Select("SELECT * FROM user WHERE status = #{status} AND age &gt; #{minAge}")
    List&lt;User&gt; selectByStatusAndAge(@Param("status") Integer status, @Param("minAge") Integer minAge);
}</pre></div>
<p class="maodian"></p><h2>mybatis知识</h2>
<p class="maodian"></p><h3># {}和${}的区别</h3>
<ul><li><code>#{}</code>:是预编译处理(PreparedStatement)。MyBatis 会将它替换为 <code>?</code>,能有效防止 SQL 注入</li><li><code>${}</code>:是字符串替换。直接将变量拼接到 SQL 中,存在注入风险,通常用于传入数据库表名或排序字段(<code>ORDER BY</code>)</li></ul>
<p class="maodian"></p><h3>MyBatis 的一级缓存和二级缓存</h3>
<ul><li><strong>一级缓存:</strong> 作用域为 <code>SqlSession</code>。在同一个会话中,相同的查询不再查数据库。默认开启。</li><li><strong>二级缓存:</strong> 作用域为 <code>Namespace</code>(多个会话间共享)。需要手动开启,且实体类必须实现序列化接口。</li><li><strong>失效场景:</strong> 任何的 <code>INSERT</code>, <code>UPDATE</code>, <code>DELETE</code> 操作都会刷新缓存</li></ul>
<p class="maodian"></p><h3>实体类属性名和表中字段名不一致怎么办?</h3>
<ol><li>在 SQL 语句中使用别名(<code>AS</code>)。</li><li>使用 <code>ResultMap</code> 进行映射(最常用)。</li><li>开启驼峰命名自动转换配置(<code>mapUnderscoreToCamelCase</code>)</li></ol>
<p class="maodian"></p><h3>Mybatis的缓存机制</h3>
<p><strong>一级缓存建议保持默认(开启),而二级缓存通常不建议开启(默认也是关闭的),尤其是在复杂的业务或分布式系统中</strong></p>
<p class="maodian"></p><h4>一级缓存</h4>
<p>一级缓存的实现参见:<code>BaseExecutor</code>、<code>PerpetualCache</code></p>
<ul><li>一级缓存的生命周期由 <code>Executor</code>(执行器)管理。在 <code>BaseExecutor</code>(所有执行器的基类)中,定义了本地缓存对象</li><li><code>SqlSession</code> 每次创建都会生成一个新的 <code>Executor</code>,所以一级缓存是线程隔离的【参见DefaultSqlSessionFactory】</li></ul>
<p class="maodian"></p><h4>二级缓存</h4>
<p>二级缓存的实现参见:<code>CachingExecutor</code>、<code>SynchronizedCache</code><br />当开启二级缓存后,MyBatis 会使用 <code>CachingExecutor</code> 包装原来的 <code>Executor</code></p>
<p><strong>装饰器模式:</strong> 二级缓存的实现非常优雅,<code>PerpetualCache</code> 是基础,通过 <code>SynchronizedCache</code>(同步)、<code>LoggingCache</code>(日志)、<code>ScheduledCache</code>(定时)、<code>SerializedCache</code>(序列化)、<code>LruCache</code>(回收策略)等装饰器层层包装,组合出最终需要的功能</p>
<table><thead><tr><th>特性</th><th>一级缓存 (Local)</th><th>二级缓存 (Global)</th></tr></thead><tbody><tr><td>作用范围</td><td>单个 SqlSession 内</td><td>同一个 Mapper 命名空间内</td></tr><tr><td>数据共享</td><td>独享,不跨会话</td><td>共享,跨 SqlSession</td></tr><tr><td>默认状态</td><td>开启</td><td>关闭 (需手动配置)</td></tr><tr><td>生命周期</td><td>伴随 SqlSession 的创建与关闭</td><td>伴随 SqlSessionFactory 的生命周期</td></tr><tr><td>清空时机</td><td>增删改操作、close()、commit()</td><td>对应 Mapper 的增删改操作、commit()</td></tr></tbody></table>
<p class="maodian"></p><h4>是否开启</h4>
<table><thead><tr><th>缓存类型</th><th>建议配置</th><th>理由</th><th>适用场景</th></tr></thead><tbody><tr><td><strong>一级缓存</strong></td><td><strong>开启 (默认)</strong></td><td>安全、自动管理、提升单次会话性能</td><td>所有场景(无需干预)</td></tr><tr><td><strong>二级缓存</strong></td><td><strong>关闭 (默认)</strong></td><td>避免脏读、避免分布式不一致</td><td>复杂业务、分布式系统</td></tr><tr><td><strong>二级缓存</strong></td><td><strong>开启 (需评估)</strong></td><td>需处理好序列化和跨表失效问题</td><td>单机应用、数据字典、纯读场景</td></tr></tbody></table>
<p><strong>一句话建议:</strong> 用好一级缓存,忘掉二级缓存,用 Redis 来做全局缓存</p>
<p class="maodian"></p><h3>加载流程</h3>
<p class="maodian"></p><h4>MapperImpl方法调用过程</h4>
<ol><li>代理拦截(MybatisMapperProxy)<ul><li><code>MybatisMapperProxy</code> 是在 Spring 容器进行依赖注入(<code>@Autowired</code>)时,由 <code>MapperProxyFactory</code> 调用 <code>getObject</code> 方法实例化的</li></ul></li><li><strong>命令执行(MapperMethod):</strong><ul><li><code>MybatisMapperProxy</code> 会拿到一个 <code>MapperMethod</code> 实例。</li><li>调用 <code>mapperMethod.execute(sqlSession, args)</code>。</li><li>此时,控制权从 &ldquo;代理层&rdquo; 转移到了 &ldquo;命令层&rdquo;</li></ul></li><li><strong>SQL 执行(SqlSession/Executor):</strong><ul><li><code>MapperMethod</code> 内部会根据 SQL 类型,调用 <code>sqlSession.selectOne()</code> 或 <code>insert()</code> 等方法。</li><li>最终由 <code>Executor</code> 去 JDBC 层面执行 SQL</li></ul></li></ol>
<div class="jb51code"><pre class="brush:java;">public class MapperRegistry {
    private final Configuration config;
    private final Map&lt;Class&lt;?&gt;, MapperProxyFactory&lt;?&gt;&gt; knownMappers = new ConcurrentHashMap();

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

    public &lt;T&gt; T getMapper(Class&lt;T&gt; type, SqlSession sqlSession) {
      MapperProxyFactory&lt;T&gt; 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);
            }
      }
    }
</pre></div>
<p class="maodian"></p><h4>Spring boot启动时初始化过程</h4>
<ol><li>SqlSessionFactoryp初始化<ul><li><code>MybatisAutoConfiguration</code> -&gt; <code>MybatisSqlSessionFactoryBean</code> -&gt;<code>SqlSessionFactory</code></li></ul></li><li>Mapper 注册与扫描<ul><li><code>AutoConfiguredMapperScannerRegistrar</code>-&gt;<code>AutoConfiguredMapperScannerRegistrar</code>-&gt;<code>MapperScannerConfigurer</code> -&gt;<code>ClassPathMapperScanner</code> -&gt;<code>ClassPathBeanDefinitionScanner</code>-&gt;<code>MapperFactoryBean</code></li></ul></li><li>MybatisMapperProxy 实例化<ol><li><strong>获取 Mapper Bean</strong>:<ul><li>当 Spring 容器尝试填充 <code>@Autowired private UserMapper userMapper;</code> 时。</li><li>容器发现这是一个 <code>FactoryBean</code>,于是调用 <strong><code>MapperFactoryBean.getObject()</code></strong>。</li></ul></li><li><strong>获取 SqlSession</strong>:<ul><li><code>MapperFactoryBean.getObject()</code> 内部会调用 <code>this.getSqlSession().getMapper(this.mapperInterface);</code>。</li></ul></li><li><strong>最终实例化 (核心点)</strong>:<ul><li><code>sqlSession.getMapper()</code> 会委托给 <strong><code>(T)this.getConfiguration().getMapper(type, this);</code></strong>。</li><li><code>Configuration</code> 持有一个 <strong><code>this.mapperRegistry.getMapper(type, sqlSession)</code></strong>(注册中心)。</li><li><code>MapperRegistry</code> 里存着 <code>Class -&gt; MapperProxyFactory</code> 的映射</li></ul></li></ol></li></ol>
<div class="jb51code"><pre class="brush:java;">public class MapperRegistry {
    private final Configuration config;
    private final Map&lt;Class&lt;?&gt;, MapperProxyFactory&lt;?&gt;&gt; knownMappers = new ConcurrentHashMap();
    public MapperRegistry(Configuration config) {
      this.config = config;
    }
    public &lt;T&gt; T getMapper(Class&lt;T&gt; type, SqlSession sqlSession) {
      MapperProxyFactory&lt;T&gt; 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);
            }
      }
    }</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026011010090867.jpg" /></p>
<p>到此这篇关于Spring boot 4 搞懂MyBatis-Plus的用法解析的文章就介绍到这了,更多相关Spring boot mybatis-plus用法内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>SpringBoot-MyBatis-plus实体类中常用的注解用法</li><li>SpringBoot整合Mybatis-plus案例及用法实例</li><li>Springboot mybatis-plus配置及用法详解</li><li>springboot2.3 整合mybatis-plus 高级功能及用法详解</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: Spring boot 4 搞懂MyBatis-Plus的用法解析