Spring Boot + MyBatis 整合与 MyBatis 原理全解析

MyBatis 是目前企业级 Java 项目使用最广泛的数据访问层框架。

相比 JPA/Hibernate,它的特点非常明显:

  • SQL 自己写,可控性强
  • 性能高、避免 ORM 过度抽象
  • 对 DBA 友好
  • 数据库主导项目的团队更倾向 MyBatis

更重要的是:
MyBatis 与 Spring Boot 的整合非常紧密,只需要极少量配置即可完成持久层搭建。

这篇文章就从"Spring Boot 整合 MyBatis"开始,讲到 MyBatis 的底层执行原理。


一、MyBatis 与 Spring Boot 是如何整合的?

在 Spring Boot 中使用 MyBatis,只需要加入一个依赖:

xml 复制代码
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>

这个 Starter 会自动完成几个关键任务:

  1. 自动创建 SqlSessionFactory
  2. 自动创建 SqlSessionTemplate
  3. 自动扫描 @Mapper 注解的接口
  4. 为这些 Mapper 接口创建代理对象
  5. 自动加载 mapper XML 文件

换句话说:

MyBatis 的整合本质上是自动装配 SqlSessionFactory + 扫描 Mapper + 生成动态代理。

我们逐个拆开来看。


二、Mapper 为什么不用写实现类?(动态代理)

写过 MyBatis 的人都知道,你只定义一个接口:

java 复制代码
@Mapper
public interface UserMapper {
    User selectById(Long id);
}

但你不写实现类,它却能正常调用:

java 复制代码
User user = userMapper.selectById(1L);

那么问题来了:

为什么一个没有实现类的接口可以被调用?

答案是:
因为 Spring Boot 帮你创建了一个 Mapper 的 JDK 动态代理对象。

核心逻辑在于:

  1. 启动时,MapperScannerRegistrar 扫描所有 @Mapper 接口
  2. 为每个接口构建一个 BeanDefinition,最终 Bean 是 MapperFactoryBean
  3. MapperFactoryBean.getObject() 返回的是一个 JDK 动态代理
  4. 代理对象拦截方法调用
  5. 根据方法名找到对应的 XML(或注解)里的 SQL
  6. 调用 SqlSession 执行 SQL
  7. 把结果映射成对象返回

本质代码逻辑类似:

java 复制代码
return Proxy.newProxyInstance(
        mapperInterface.getClassLoader(),
        new Class[]{mapperInterface},
        new MapperProxy(sqlSession, mapperInterface, methodCache)
);

你调用:

java 复制代码
userMapper.selectById(1)

底层发生的是:

  • JDK 代理的 invoke() 被触发
  • 找到 XML 中 <select id="selectById">
  • 封装参数,执行 SQL
  • 映射结果并返回

这就是 MyBatis 的魔法:"接口即 Mapper"。


三、MyBatis 的执行流程(非常重要)

在面试中如果问到 MyBatis 原理,你只要完整说出下面这段流程,面试官基本会认可你"真的懂"。

一个 MyBatis 查询的大致流程如下:

1. 创建 SqlSession

Spring Boot 会创建一个 SqlSessionTemplate,这个模板对象是线程安全的,内部持有一个 SqlSession。

2. 获取 Mapper 的代理对象

通过 JDK 动态代理生成 MapperProxy。

3. 代理拦截方法调用

MapperProxy.invoke() 被触发,然后从配置中找到对应的 MappedStatement。

每个 SQL 对应一个唯一 ID,例如:

复制代码
namespace + id

比如:

UserMapper.xml 中的 <select id="selectById">

会在启动时注册为:

复制代码
com.xxx.UserMapper.selectById

4. 封装参数

参数采用 ParameterHandler 封装。

5. 调用 Executor 执行 SQL

MyBatis 有三种 Executor:

  • SimpleExecutor
  • BatchExecutor
  • ReuseExecutor

默认使用 SimpleExecutor。

6. 调用 StatementHandler 执行 SQL

生成 PreparedStatement

绑定参数

执行 SQL

7. 调用 ResultSetHandler 处理结果集

把查询结果转换为 Java 对象:

  • 字段映射
  • 下划线转驼峰
  • 关联查询
  • 类型转换
  • 自动处理 List、Map 等结构

最终返回结果。

总结一句话:

MyBatis 的执行流程就是:

Mapper 代理 → 找 SQL → Executor → StatementHandler → JDBC → ResultSetHandler。

这是你在面试中必须能讲顺的一段。


四、MyBatis 的动态 SQL 原理:SQLSource + Ognl

MyBatis 的动态 SQL 功能非常强,比如:

xml 复制代码
<select id="selectUser">
    SELECT * FROM user
    <where>
        <if test="name != null">
            AND name = #{name}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
    </where>
</select>

面试官可能问:

MyBatis 的动态 SQL 是怎么实现的?

底层逻辑如下:

  1. MyBatis 启动时解析 XML,把动态标签解析成一个"节点树"(SqlNode)
  2. 执行 SQL 时,根据当前传入参数,动态拼接 SQL
  3. 使用 Ognl 表达式计算条件(如 test="name != null")
  4. 最终生成 BoundSql,交给 JDBC 执行

也就是说:

MyBatis 的动态 SQL 是在运行期,根据参数动态生成 SQL,而不是在编译期生成。

这使得 SQL 更灵活,也更可读。


五、MyBatis 缓存机制(一级缓存 & 二级缓存)

缓存是 MyBatis 的另一个核心点。

1. 一级缓存(本地缓存)

默认开启,SqlSession 级别。

如果在同一个 SqlSession 中重复执行同样的查询,MyBatis 会直接从缓存返回结果:

java 复制代码
User u1 = mapper.selectById(1);
User u2 = mapper.selectById(1);

两次查询只会打一次数据库。

特点:

  • 默认开启
  • 生命周期与 SqlSession 相同
  • 任何更新操作都会清空一级缓存
  • Spring 项目中,每个 HTTP 请求通常对应一个 SqlSession

2. 二级缓存(Mapper namespace 级别)

默认关闭,需要手动开启。

它的特点:

  • 基于 Mapper namespace
  • 多个 SqlSession 可以共享缓存
  • 必须开启 <cache /> 标签
  • 查询结果必须可序列化
  • 注意与数据库一致性问题

简单示例:

xml 复制代码
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="false"/>

使用二级缓存必须谨慎,因为:

  • 多线程更新可能导致脏数据
  • 企业项目一般开启 Redis 等外部缓存,因此 MyBatis 二级缓存通常不使用

六、MyBatis 插件机制(Interceptor)

插件机制是 MyBatis 的高级扩展能力,可以拦截四大对象:

  • Executor(执行器)
  • ParameterHandler(参数处理器)
  • StatementHandler(语句处理器)
  • ResultSetHandler(结果集处理器)

你可以实现:

java 复制代码
@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {})
})
public class MyPlugin implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 前置逻辑
        Object result = invocation.proceed();
        // 后置逻辑
        return result;
    }
}

插件机制常用于:

  • SQL 打印
  • 分页(如 PageHelper)
  • 性能监控
  • 审计字段(自动填充 created_at/updated_at)

这一块在面试中属于"加分项"。


七、Spring Boot 与 MyBatis 整合的自动装配流程

当加入 MyBatis Starter 时,Spring Boot 会做以下事:

1. 创建 SqlSessionFactory

读取:

  • 数据源配置
  • MyBatis 配置(mybatis-config.xml)
  • Mapper XML 文件
  • 类型别名

2. 创建 SqlSessionTemplate(线程安全)

这是你操作数据库的中心入口。

3. 扫描 Mapper 接口

根据:

  • @Mapper
  • @MapperScan

生成 BeanDefinition。

4. 为 Mapper 生成代理对象

使用 MapperProxyFactory 自动生成 JDK 动态代理。

5. 加载并解析 XML

每个 Mapper XML 中的 <select>, <update>, <insert>, <delete> 都会生成对应的 MappedStatement。

6. 整合事务管理

Spring Boot 自动整合 DataSourceTransactionManager,实现声明式事务(@Transactional)。

这一整套流程保证了 MyBatis 在 Spring Boot 中几乎是"零配置可用"。


八、MyBatis 常见面试高频题总结

下面这些问题,都是 MyBatis 面试必问的:

1. MyBatis 的核心原理是什么?

  • Mapper 是动态代理
  • SQL 映射到 MappedStatement
  • Executor 执行 SQL
  • StatementHandler 处理 JDBC
  • ResultSetHandler 映射结果

2. 一级缓存与二级缓存的区别?

  • 一级缓存:SqlSession 范围,默认开启
  • 二级缓存:Mapper namespace 范围,需要手动开启

3. MyBatis 动态 SQL 如何实现?

  • XML 解析为 SqlNode 树
  • 执行时根据参数动态拼接 SQL
  • 使用 Ognl 解析 test 表达式

4. MyBatis 怎么执行一条 SQL?

Mapper 代理 → Executor → StatementHandler → JDBC → ResultSetHandler

5. 为什么 Mapper 接口没有实现类也能工作?

因为 Spring Boot 使用 JDK 动态代理为 Mapper 创建代理对象。

6. MyBatis 插件能拦截什么?

Executor

ParameterHandler

StatementHandler

ResultSetHandler


九、总结

MyBatis 在企业中之所以如此流行,原因在于:

  1. SQL 自己写 ------ 更灵活,性能可控
  2. 与数据库结构保持强一致性
  3. 容易调试、容易排查问题
  4. 与 Spring Boot 集成之后几乎零配置可用
  5. Mapper 动态代理 + XML/注解映射机制让开发非常高效

掌握了:

  • Mapper 动态代理
  • MyBatis 的执行流程
  • 缓存机制
  • 动态 SQL 原理
  • 插件机制
  • Spring Boot 自动装配流程

你基本就已经掌握了 MyBatis 的核心体系。

相关推荐
q***46522 小时前
在2023idea中如何创建SpringBoot
java·spring boot·后端
q***13612 小时前
十七:Spring Boot依赖 (2)-- spring-boot-starter-web 依赖详解
前端·spring boot·后端
q***25212 小时前
Spring Boot接收参数的19种方式
java·spring boot·后端
WX-bisheyuange2 小时前
基于Spring Boot的民谣网站的设计与实现
java·spring boot·后端
倚肆2 小时前
Spring Boot 日志系统全面详解
spring boot·junit·单元测试
q***14642 小时前
Spring Boot文件上传
java·spring boot·后端
Run_Teenage3 小时前
C++:智能指针的使用及其原理
开发语言·c++·算法
WX-bisheyuange4 小时前
基于Spring Boot的民宿预定系统的设计与实现
java·spring boot·后端·毕业设计
码界奇点5 小时前
Java设计模式精讲从基础到实战的常见模式解析
java·开发语言·设计模式·java-ee·软件工程