沉底一下对传统技术栈,mybatis的认识
先讲个基本的点。
我们通过一个流程,对mybatis的企业开发中的常见流程。
1.引入依赖
2.在配置类,或者yaml中对mybatis做配置
3.创建mapper接口类,以及xml文件
4.在test中测试一下,写的接口写的有没有问题。
对于中间有很多细节只是点可以深挖
1.mybatis有哪些领域,是要在yaml,或者config文件包下做配置的。
2.动态sql
3.分页插件和逻辑
4.如何在mybatis中写复杂的sql
这些先不作为重点
我们研究一下mybatis下的原理层面,要认识的信息领域,
1.mybatis的执行原理是什么
2.对于mybatis的插件相关的知识
3.mybatis的缓存机制
4.mybatisd的连接池机制
5,mybatis有哪些机制可以实现分页,原理是什么
一、先讲基本点:MyBatis 企业开发标准流程
你列的 4 步完全正确,补充一个典型顺序:
1️⃣ 引入依赖(Spring Boot 场景)
xml
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<!-- 如果要用连接池监控,再加 Druid -->
2️⃣ 在 yaml 中做核心配置
yaml
mybatis:
mapper-locations: classpath:mapper/*.xml # xml位置
type-aliases-package: com.demo.entity # 实体类短名
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true # 自动驼峰
lazy-loading-enabled: true # 延迟加载开关
3️⃣ 创建 Mapper 接口 + XML
java
@Mapper
public interface UserMapper {
User selectById(Long id);
}
xml
<mapper namespace="com.demo.mapper.UserMapper">
<select id="selectById" resultType="User">
select * from user where id = #{id}
</select>
</mapper>
4️⃣ 测试
java
@SpringBootTest
class UserMapperTest {
@Autowired UserMapper userMapper;
@Test void test() {
System.out.println(userMapper.selectById(1L));
}
}
✅ 这是 99% 项目每天都在做的事。
二、沉底:MyBatis 原理层 5 大核心领域
你现在真正想深挖的是这 5 个,我逐个给你讲"是什么 → 为什么 → 怎么实现"。
1️⃣ MyBatis 执行原理(最重要)
一句话:
通过 动态代理 把 Mapper 接口和 SQL 绑定,通过 Executor + StatementHandler 执行 SQL 并映射结果。
核心流程(背下来):
Mapper 接口
↓ 动态代理
MapperProxy
↓ 调用 MapperMethod
SqlSession → Executor → StatementHandler → ParameterHandler → ResultSetHandler
角色解释:
| 组件 | 职责 |
|---|---|
MapperProxy |
为你的 UserMapper 接口生成代理对象 |
MapperMethod |
解析接口方法 → 找 XML 中的 SQL |
SqlSession |
对外提供增删改查 API |
Executor |
执行器(缓存、事务、批量) |
StatementHandler |
处理 JDBC Statement |
ParameterHandler |
设置参数 |
ResultSetHandler |
把 ResultSet 映射成对象 |
一个 SQL 执行的真实路径:
java
userMapper.selectById(1)
→ MapperProxy.invoke()
→ MapperMethod.execute()
→ SqlSession.selectOne()
→ CachingExecutor.query()
→ SimpleExecutor.doQuery()
→ StatementHandler.query()
→ ResultSetHandler.handleResultSets()
👉 这是 MyBatis 源码阅读的入口线。
2️⃣ MyBatis 插件原理(你已经接触过)
核心三要素:
Interceptor:你写的插件逻辑Invocation:当前执行方法的包装(目标、参数、方法)Plugin:用 JDK 动态代理把多个拦截器串成责任链
最经典的插件:
- 分页插件(PageHelper)
- 数据权限插件
- 慢 SQL 监控插件
插件能拦截的四大对象:
Executor → 执行器(分页、缓存)
StatementHandler → SQL 处理(改写 SQL)
ParameterHandler → 参数处理
ResultSetHandler → 结果处理(脱敏、加密)
👉 插件的本质是责任链 + 动态代理。
3️⃣ MyBatis 缓存机制(一级缓存 + 二级缓存)
| 缓存级别 | 作用范围 | 是否默认开启 | 典型问题 |
|---|---|---|---|
| 一级缓存 | SqlSession 内 | ✅ 开启 | 同一个 Session 多次查询走缓存 |
| 二级缓存 | Mapper 级别 | ❌ 关闭 | 跨 Session 共享,易脏读 |
一级缓存(一定要懂):
java
User u1 = mapper.selectById(1); // 查 DB
User u2 = mapper.selectById(1); // 走缓存
// 注意:执行 insert/update/delete 会清空缓存
二级缓存(谨慎开启):
xml
<cache/>
- 实体必须序列化
- 多表查询时,一张表更新,另一张表缓存不会失效 → 脏数据
👉 真实生产:大部分项目只靠一级缓存 + Redis。
4️⃣ MyBatis 连接池机制
关键点 :MyBatis 本身不实现连接池,而是包装 JDBC 的 DataSource。
常见连接池:
- HikariCP(Spring Boot 默认,最快)
- Druid(功能最强,有监控)
- Tomcat JDBC Pool
MyBatis 与连接池的关系:
MyBatis 配置 DataSource
↓
Spring 管理 Hikari/Druid
↓
MyBatis 每次执行 SQL 时:
dataSource.getConnection()
→ 从连接池借连接
→ 执行 SQL
→ 归还连接
一个典型配置:
yaml
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
👉 MyBatis 只消费连接池,不生产连接池。
5️⃣ MyBatis 分页机制(多种实现)
你问"有哪些机制可以实现分页",这是好问题:
| 分页方式 | 原理 | 优缺点 |
|---|---|---|
| 物理分页(推荐) | 改写 SQL:limit ?,? |
性能好,不走内存 |
| 逻辑分页(内存分页) | 查全部数据,Java 中截取 | 性能极差,禁用 |
| 数组分页 | 同上 | ❌ 不用于生产 |
物理分页的 3 种实现:
1️⃣ PageHelper 插件(最常用)
java
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectAll();
PageInfo page = new PageInfo(list);
原理:拦截 Executor,改写 SQL 增加 limit
2️⃣ 手写 limit
xml
<select id="selectPage">
select * from user limit #{offset}, #{pageSize}
</select>
3️⃣ 分页拦截器自定义
- 自己实现 Interceptor
- 识别特定参数(如
PageParam) - 动态改写 SQL
👉 结论:PageHelper 覆盖 90% 场景,手写 limit 适合极简项目。
三、你现在可以形成的"MyBatis 原理地图"
用户调用 Mapper 接口
↓
【动态代理】MapperProxy
↓
【执行入口】SqlSession
↓
【拦截器链】Plugin(责任链)
↓
【核心执行】Executor(一级缓存在这里)
↓
【可选】二级缓存
↓
【SQL 处理】StatementHandler
↓
【参数】ParameterHandler
↓
【JDBC】getConnection(从连接池)
↓
【结果映射】ResultSetHandler