欢迎光临
我们一直在努力

彻底吃透MyBatis核心原理:SqlSession、两级缓存、Spring集成机制一次说清吃透

本文作者:CodeStats
一个专注分享Java底层原理、自研框架实战干货的技术博主
如果觉得内容实用,欢迎 点赞 + 收藏 + 关注!


📖 目录

  • 一、写在前面

  • 二、SqlSession:MyBatis的核心入口

  • 三、生命周期:非线程安全,禁止共享

  • 四、两级缓存机制(高频面试+生产重点)

  • 五、核心组件与完整执行流程

  • 六、分页机制:为什么不用RowBounds?

  • 七、Spring集成原理:SqlSessionTemplate的秘密

  • 八、自研框架模拟MyBatis事务

  • 九、核心速查表

  • 十、写在最后


一、写在前面

日常开发中,我们几乎每天都会用到MyBatis。下面这段基础代码,想必所有Java开发者都见过:

java

SqlSession sqlSession = sqlSessionFactory.openSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectById(1);
sqlSession.commit();
} finally {
sqlSession.close();
}

很多人只会机械套用这段代码,但很少有人深究:

  • SqlSession的本质是什么?

  • 它的生命周期到底该如何把控?

  • 事务提交、会话关闭的底层意义是什么?

本文将从底层逻辑出发,逐一拆解MyBatis最核心的SqlSession会话、两级缓存、Spring集成机制,帮你彻底吃透MyBatis核心原理,告别只会用、不懂原理的开发困境。


二、SqlSession:MyBatis的核心入口

用最通俗的话总结:

SqlSession是MyBatis与数据库交互的一次性工作单元,也是MyBatis操作数据库的核心入口。

2.1 SqlSession的四大核心职责

职责说明
1️⃣ 执行SQL语句 查询、新增、修改、删除
2️⃣ 获取Mapper代理对象 通过动态代理返回接口实现
3️⃣ 管理数据库事务 开启、提交、回滚、关闭
4️⃣ 维护一级缓存 会话级别的本地缓存

三、生命周期:非线程安全,禁止共享

3.1 核心原则(⭐最重要)

SqlSession不具备线程安全性,多个线程不能共用同一个SqlSession。

违反这一原则会导致:数据错乱 + 事务异常 + 诡异Bug

3.2 标准使用规范

场景使用策略
单次数据库操作 单独创建一个SqlSession
Web项目 一个HTTP请求对应一个独立SqlSession
多线程场景 每个线程持有自己的SqlSession,互不干扰

3.3 完整生命周期流程图

text

应用启动

SqlSessionFactory(全局单例,只创建一次)

每次数据库操作 → openSession() → 创建新的SqlSession

获取Mapper代理对象 → 执行SQL

commit / rollback

close()(必须!)

3.4 ✅ 正确 vs ❌ 错误示例

✅ 正确写法:try-with-resources(推荐)

java

// 自动关闭会话,无需手动finally
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
return mapper.selectById(id);
}

❌ 错误写法:会话未关闭(连接泄漏)

java

// 高危:会话未关闭,长期堆积会导致连接池耗尽
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
return mapper.selectById(id); // 无close()操作,资源无法释放

⚠️ 后果:数据库连接池耗尽 → 应用假死 → 线上事故


四、两级缓存机制(高频面试+生产重点)

MyBatis内置两级缓存,用于减少数据库查询次数、提升接口性能。

4.1 一级缓存(SqlSession级别)

核心特性:

属性说明
默认状态 ✅ 默认开启,无法关闭
作用范围 仅当前同一个SqlSession内有效
底层实现 PerpetualCache,本质是HashMap
生命周期 会话创建 → 缓存创建;会话关闭 → 缓存销毁
演示案例

java

try (SqlSession session = factory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);

User user1 = mapper.selectById(1); // 第一次查询,访问数据库
User user2 = mapper.selectById(1); // 第二次查询,命中一级缓存

System.out.println(user1 == user2); // 输出 true(同一内存地址)
}

❌ 一级缓存失效场景(重点)
场景说明
执行增删改操作 INSERT / UPDATE / DELETE
手动清空缓存 调用clearCache()
事务提交/回滚 commit() / rollback()
会话关闭 close()

4.2 二级缓存(Mapper级别)

核心特性:

属性说明
默认状态 ❌ 默认关闭,需手动开启
作用范围 全局同一个Mapper接口下,跨会话共享
数据持久 会话关闭后缓存依然存在(应用级别)
实体类要求 必须实现Serializable序列化接口
开启二级缓存(两步)

第一步:全局配置文件开启总开关

xml

<!– mybatis-config.xml –>
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>

第二步:Mapper.xml开启缓存

xml

<mapper namespace="com.example.UserMapper">
<cache
eviction="LRU" <!– 淘汰策略:最近最少使用 –>
flushInterval="60000" <!– 60秒刷新一次 –>
size="512" <!– 最多缓存512个对象 –>
readOnly="false"/> <!– 非只读,可返回克隆对象 –>
</mapper>

4.3 两级缓存核心对比

对比项一级缓存二级缓存
作用范围 单个SqlSession会话 跨SqlSession,全局Mapper级别
默认状态 ✅ 开启(不可关闭) ❌ 关闭(需手动开启)
生命周期 会话结束即销毁 应用运行期间持续存在
共享性 线程/会话私有 全局共享
实体类要求 无要求 必须实现Serializable

五、核心组件与完整执行流程

5.1 核心组件职责一览

组件职责
SqlSessionFactory 会话工厂,全局单例,唯一职责:创建SqlSession
SqlSession 数据库会话,SQL执行 + 事务管理 + 缓存维护
Executor SQL执行器,负责缓存校验、SQL执行、事务调度
MappedStatement 封装SQL语句、参数规则、结果映射规则
StatementHandler 负责JDBC Statement创建、参数赋值、SQL执行
ParameterHandler 处理SQL参数的类型转换与赋值
ResultSetHandler 处理结果集,封装为Java实体对象
TypeHandler 实现Java类型 ↔ 数据库字段类型转换

5.2 完整组件交互流程图

text

开发者调用 Mapper 接口方法

获取 Mapper 动态代理对象(JDK代理)

代理对象调用 SqlSession 查询/更新方法

┌─────────────────────────────────────┐
│ Executor 执行器 │
│ ① 校验二级缓存 → ② 校验一级缓存 │
└─────────────────────────────────────┘
↓(缓存未命中)
StatementHandler 执行真实SQL

ParameterHandler 注入参数

JDBC 执行 SQL

ResultSetHandler 封装结果集

结果存入缓存 → 返回给开发者


六、分页机制:为什么不用RowBounds?

6.1 原生RowBounds(❌ 不推荐)

问题本质:内存分页,不会修改原生SQL

java

// 不推荐:会查询全量数据后在内存中截取
RowBounds rowBounds = new RowBounds(offset, limit);
List<User> list = session.selectList("selectAll", null, rowBounds);

⚠️ 严重缺陷:数据量大时 → 全量查库 → 内存溢出 → 性能卡顿

6.2 PageHelper物理分页(✅ 生产推荐)

核心原理:基于MyBatis插件机制,直接改写底层SQL

java

// 推荐:仅查询当前页数据
PageHelper.startPage(pageNum, pageSize);
List<User> list = userMapper.selectAll();
PageInfo<User> pageInfo = new PageInfo<>(list);

SQL改写效果对比:

sql

— 原始SQL
SELECT * FROM user

— PageHelper改写后(MySQL)
SELECT * FROM user LIMIT 0, 10

PageHelper核心原理(三步):

  • 通过ThreadLocal获取当前线程的分页参数

  • 根据数据库类型动态拼接LIMIT / ROWNUM

  • SQL执行完成后自动清除ThreadLocal,避免参数污染


  • 七、Spring集成原理:SqlSessionTemplate的秘密

    7.1 核心类:SqlSessionTemplate

    Spring并未直接使用原生SqlSession,而是通过SqlSessionTemplate封装,实现会话自动管理 + 事务同步 + 线程安全适配。

    SqlSessionTemplate本质是SqlSession的JDK动态代理对象。

    7.2 对象依赖链路(Spring容器启动时)

    text

    SqlSessionFactoryBean(配置加载)

    SqlSessionFactory(全局单例)

    MapperFactoryBean(为每个Mapper接口生成)

    Mapper代理对象(JDK动态代理)

    SqlSessionTemplate(代理原生SqlSession)

    DefaultSqlSession(原生会话)

    Executor执行器

    7.3 日常开发:直接@Autowired注入

    配置类:

    java

    @Configuration
    @MapperScan("com.example.mapper") // 扫描Mapper接口包
    public class MyBatisConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource);
    return factoryBean.getObject();
    }
    }

    业务使用:

    java

    @Service
    public class UserService {

    @Autowired
    private UserMapper userMapper; // Spring自动注入代理对象

    public User getUser(Integer id) {
    return userMapper.selectById(id); // 无需手动创建会话
    }
    }

    自动注入核心流程:

  • @MapperScan扫描指定包下所有Mapper接口

  • 为每个接口生成MapperFactoryBean工厂Bean

  • 工厂Bean通过SqlSessionTemplate获取JDK动态代理对象

  • 代理对象交由Spring容器管理,实现自动注入


  • 八、自研框架模拟MyBatis事务

    为了更透彻理解MyBatis事务与代理原理,我们通过自研框架模拟声明式事务的实现逻辑。

    8.1 自定义事务注解

    java

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Transactional {
    Propagation propagation() default Propagation.REQUIRED;
    }

    8.2 事务动态代理实现

    java

    public class TransactionProxy implements InvocationHandler {

    private final Object target;
    private final JdbcTemplate jdbcTemplate;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    // 判断方法是否添加事务注解
    if (method.isAnnotationPresent(Transactional.class)) {
    try {
    jdbcTemplate.beginTransaction(); // 开启事务
    Object result = method.invoke(target, args);
    jdbcTemplate.commit(); // 提交事务
    return result;
    } catch (Exception e) {
    jdbcTemplate.rollback(); // 回滚事务
    throw e;
    }
    }

    // 无事务注解,直接执行
    return method.invoke(target, args);
    }
    }

    8.3 事务功能使用示例

    java

    @Service
    public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Transactional // 添加事务注解,方法内所有操作纳入同一事务
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
    userMapper.decreaseBalance(fromId, amount);
    userMapper.increaseBalance(toId, amount);
    }
    }

    8.4 自研框架 vs MyBatis 核心对比

    核心特性原生MyBatisCodeStats自研框架
    会话管理 SqlSession JdbcTemplate + 动态代理
    事务控制 手动commit/rollback + Spring声明式 自定义@Transactional注解
    Mapper代理 JDK动态代理 JDK动态代理
    连接池 适配HikariCP等 自研SimpleDataSource
    SQL执行 StatementHandler处理器 直接基于JdbcTemplate

    九、核心速查表

    知识点核心结论
    SqlSession 非线程安全,单请求单会话,用完必须close()
    一级缓存 默认开启、会话级别HashMap,增删改/提交后自动清空
    二级缓存 手动开启、Mapper全局共享,实体类需实现Serializable
    分页机制 摒弃RowBounds,生产统一用PageHelper物理分页
    Spring集成 SqlSessionTemplate代理会话,自动管理生命周期
    事务原理 基于动态代理 + 注解,实现声明式事务

    十、写在最后

    MyBatis作为Java生态主流的ORM框架,熟练使用只是基础。

    掌握底层的会话机制、缓存原理、Spring集成逻辑,才能:

    • ✅ 规避缓存失效、事务异常、连接泄漏等常见问题

    • ✅ 写出更健壮、更高效的数据库访问代码

    • ✅ 在面试中轻松应对底层原理追问

    • ✅ 迈出高级开发工程师的关键一步


    💬 互动话题

    你在开发中遇到过哪些因为不懂MyBatis底层原理而踩的坑?

    • 一级缓存导致的“数据读不到”?

    • 二级缓存序列化报错?

    • 还是事务不回滚的诡异Bug?

    欢迎在评论区分享你的经历,一起交流学习、共同进步!


    📌 如果本文对你有帮助,欢迎:

    • 👍 点赞 支持原创

    • ⭐ 收藏 方便回看

    • 🔁 转发 给需要的朋友

    • 👀 关注 CodeStats,陪你深度进阶技术!


    CodeStats —— 专注Java底层原理与自研框架实践

    赞(0)
    未经允许不得转载:171主机测评 » 彻底吃透MyBatis核心原理:SqlSession、两级缓存、Spring集成机制一次说清吃透
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址