彻底吃透MyBatis核心原理:SqlSession、两级缓存、Spring集成机制一次说清吃透 本文作者CodeStats一个专注分享Java底层原理、自研框架实战干货的技术博主如果觉得内容实用欢迎点赞 收藏 关注 目录一、写在前面二、SqlSessionMyBatis的核心入口三、生命周期非线程安全禁止共享四、两级缓存机制高频面试生产重点五、核心组件与完整执行流程六、分页机制为什么不用RowBounds七、Spring集成原理SqlSessionTemplate的秘密八、自研框架模拟MyBatis事务九、核心速查表十、写在最后一、写在前面日常开发中我们几乎每天都会用到MyBatis。下面这段基础代码想必所有Java开发者都见过javaSqlSession 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核心原理告别只会用、不懂原理的开发困境。二、SqlSessionMyBatis的核心入口用最通俗的话总结SqlSession是MyBatis与数据库交互的一次性工作单元也是MyBatis操作数据库的核心入口。2.1 SqlSession的四大核心职责职责说明1️⃣ 执行SQL语句查询、新增、修改、删除2️⃣ 获取Mapper代理对象通过动态代理返回接口实现3️⃣ 管理数据库事务开启、提交、回滚、关闭4️⃣ 维护一级缓存会话级别的本地缓存三、生命周期非线程安全禁止共享3.1 核心原则⭐最重要SqlSession不具备线程安全性多个线程不能共用同一个SqlSession。违反这一原则会导致数据错乱 事务异常 诡异Bug3.2 标准使用规范场景使用策略单次数据库操作单独创建一个SqlSessionWeb项目一个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生命周期会话创建 → 缓存创建会话关闭 → 缓存销毁演示案例javatry (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 namecacheEnabled valuetrue/ /settings第二步Mapper.xml开启缓存xmlmapper namespacecom.example.UserMapper cache evictionLRU !-- 淘汰策略最近最少使用 -- flushInterval60000 !-- 60秒刷新一次 -- size512 !-- 最多缓存512个对象 -- readOnlyfalse/ !-- 非只读可返回克隆对象 -- /mapper4.3 两级缓存核心对比对比项一级缓存二级缓存作用范围单个SqlSession会话跨SqlSession全局Mapper级别默认状态✅ 开启不可关闭❌ 关闭需手动开启生命周期会话结束即销毁应用运行期间持续存在共享性线程/会话私有全局共享实体类要求无要求必须实现Serializable五、核心组件与完整执行流程5.1 核心组件职责一览组件职责SqlSessionFactory会话工厂全局单例唯一职责创建SqlSessionSqlSession数据库会话SQL执行 事务管理 缓存维护ExecutorSQL执行器负责缓存校验、SQL执行、事务调度MappedStatement封装SQL语句、参数规则、结果映射规则StatementHandler负责JDBCStatement创建、参数赋值、SQL执行ParameterHandler处理SQL参数的类型转换与赋值ResultSetHandler处理结果集封装为Java实体对象TypeHandler实现Java类型 ↔ 数据库字段类型转换5.2 完整组件交互流程图text开发者调用 Mapper 接口方法 ↓ 获取 Mapper 动态代理对象JDK代理 ↓ 代理对象调用 SqlSession 查询/更新方法 ↓ ┌─────────────────────────────────────┐ │ Executor 执行器 │ │ ① 校验二级缓存 → ② 校验一级缓存 │ └─────────────────────────────────────┘ ↓缓存未命中 StatementHandler 执行真实SQL ↓ ParameterHandler 注入参数 ↓ JDBC 执行 SQL ↓ ResultSetHandler 封装结果集 ↓ 结果存入缓存 → 返回给开发者六、分页机制为什么不用RowBounds6.1 原生RowBounds❌ 不推荐问题本质内存分页不会修改原生SQLjava// 不推荐会查询全量数据后在内存中截取 RowBounds rowBounds new RowBounds(offset, limit); ListUser list session.selectList(selectAll, null, rowBounds);⚠️严重缺陷数据量大时 → 全量查库 → 内存溢出 → 性能卡顿6.2 PageHelper物理分页✅ 生产推荐核心原理基于MyBatis插件机制直接改写底层SQLjava// 推荐仅查询当前页数据 PageHelper.startPage(pageNum, pageSize); ListUser list userMapper.selectAll(); PageInfoUser pageInfo new PageInfo(list);SQL改写效果对比sql-- 原始SQL SELECT * FROM user -- PageHelper改写后MySQL SELECT * FROM user LIMIT 0, 10PageHelper核心原理三步通过ThreadLocal获取当前线程的分页参数根据数据库类型动态拼接LIMIT/ROWNUMSQL执行完成后自动清除ThreadLocal避免参数污染七、Spring集成原理SqlSessionTemplate的秘密7.1 核心类SqlSessionTemplateSpring并未直接使用原生SqlSession而是通过SqlSessionTemplate封装实现会话自动管理 事务同步 线程安全适配。SqlSessionTemplate本质是SqlSession的JDK动态代理对象。7.2 对象依赖链路Spring容器启动时textSqlSessionFactoryBean配置加载 ↓ SqlSessionFactory全局单例 ↓ MapperFactoryBean为每个Mapper接口生成 ↓ Mapper代理对象JDK动态代理 ↓ SqlSessionTemplate代理原生SqlSession ↓ DefaultSqlSession原生会话 ↓ Executor执行器7.3 日常开发直接Autowired注入配置类javaConfiguration 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(); } }业务使用javaService 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 自定义事务注解javaTarget(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface Transactional { Propagation propagation() default Propagation.REQUIRED; }8.2 事务动态代理实现javapublic 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 事务功能使用示例javaService 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自研框架会话管理SqlSessionJdbcTemplate 动态代理事务控制手动commit/rollback Spring声明式自定义Transactional注解Mapper代理JDK动态代理JDK动态代理连接池适配HikariCP等自研SimpleDataSourceSQL执行StatementHandler处理器直接基于JdbcTemplate九、核心速查表知识点核心结论SqlSession非线程安全单请求单会话用完必须close()一级缓存默认开启、会话级别HashMap增删改/提交后自动清空二级缓存手动开启、Mapper全局共享实体类需实现Serializable分页机制摒弃RowBounds生产统一用PageHelper物理分页Spring集成SqlSessionTemplate代理会话自动管理生命周期事务原理基于动态代理 注解实现声明式事务十、写在最后MyBatis作为Java生态主流的ORM框架熟练使用只是基础。掌握底层的会话机制、缓存原理、Spring集成逻辑才能✅ 规避缓存失效、事务异常、连接泄漏等常见问题✅ 写出更健壮、更高效的数据库访问代码✅ 在面试中轻松应对底层原理追问✅ 迈出高级开发工程师的关键一步 互动话题你在开发中遇到过哪些因为不懂MyBatis底层原理而踩的坑一级缓存导致的“数据读不到”二级缓存序列化报错还是事务不回滚的诡异Bug欢迎在评论区分享你的经历一起交流学习、共同进步 如果本文对你有帮助欢迎点赞支持原创⭐收藏方便回看转发给需要的朋友关注CodeStats陪你深度进阶技术CodeStats—— 专注Java底层原理与自研框架实践