《Java 100 天进阶之路》第86篇:MyBatis 核心原理与缓存避坑指南(2026版)
第86篇MyBatis 核心原理与缓存避坑指南2026版系列导航《Java 100 天进阶之路 | 从入门到上岗就业》完整目录 |⬅️ 上一篇第85篇SQL优化实战 |➡️ 下一篇第87篇MyBatis源码阅读待发布核心导读本文深度剖析 MyBatis 核心执行流程、#{}与${}底层区别、一二级缓存陷阱并结合 Spring Boot 与 MyBatis-Plus 给出 2026 年最新实战方案。文末附高频面试题与避坑指南助你轻松拿下“大厂” Offer一、核心知识点MyBatis 核心组件SqlSessionFactory、SqlSession、Executor、StatementHandler、ParameterHandler、ResultSetHandler执行流程加载配置 → 创建 SqlSession → 获取 Mapper 代理 → 执行 SQL → 结果映射动态 SQLif、choose、where、foreach等标签解析原理#{} vs ${}预编译占位符 vs 字符串替换区别与风险缓存机制一级缓存SqlSession 级别、二级缓存Mapper 级别Spring Boot 整合application.yml配置、MapperScan、MybatisAutoConfigurationMyBatis-Plus 扩展BaseMapper、代码生成器、MybatisPlusInterceptor分页插件二、通俗讲解1分钟开心学1. MyBatis 是什么MyBatis 是一个半自动 ORM 框架它把 SQL 与 Java 代码解耦让你在 XML 或注解中写 SQL由框架自动完成结果映射。生活类比JDBC 就像你亲自动手洗菜、切菜、炒菜、洗碗全部手写。MyBatis 就像请了个帮厨你只需写好菜谱SQL他帮你洗、切、炒最后把菜端上来映射成对象。2. 核心组件职责组件职责SqlSessionFactory生产 SqlSession 的工厂每个数据库环境一个SqlSession数据库会话代表一次连接包含执行 SQL 的方法Executor执行器负责 SQL 执行、缓存管理StatementHandler处理 JDBC Statement 的创建和参数设置ParameterHandler处理参数映射ResultSetHandler处理结果集映射到 Java 对象3. #{} vs ${}#{}预编译占位符相当于PreparedStatement的?可防止 SQL 注入。${}纯字符串替换相当于直接拼接 SQL有注入风险仅用于表名、列名等动态场景。口诀参数用#{}防注入保安全表名列名用${}动态拼接需谨慎。4. 一级缓存与二级缓存一级缓存默认开启作用域为 SqlSession。同一个会话中执行相同 SQL 不重复查询数据库。但执行增删改会清空缓存。二级缓存作用域为 Mappernamespace需手动配置cache/多个 SqlSession 共享。存在脏读风险多表关联查询慎用。三、实操代码案例 场景说明测试表用户表user。CREATETABLEuser(idintNOTNULLAUTO_INCREMENT,namevarchar(50)DEFAULTNULL,ageintDEFAULTNULL,PRIMARYKEY(id));3.1 Spring Boot 配置2026 推荐方式application.ymlmybatis:# 实体类包别名简化 resultType 写法type-aliases-package:com.example.entity# mapper XML 文件位置mapper-locations:classpath:mapper/*.xmlconfiguration:# 开启驼峰命名映射user_name → userNamemap-underscore-to-camel-case:true# 日志实现log-impl:org.apache.ibatis.logging.slf4j.Slf4jImpl# 开启二级缓存默认 falsecache-enabled:false启动类添加MapperScanSpringBootApplicationMapperScan(com.example.mapper)// 扫描 Mapper 接口publicclassApplication{publicstaticvoidmain(String[]args){SpringApplication.run(Application.class,args);}}原理简述Spring Boot 的MybatisAutoConfiguration自动装配类会根据上述配置创建SqlSessionFactory和SqlSessionTemplate并将 Mapper 接口动态代理成 Spring Bean。3.2 传统 XML 方式保留适用复杂 SQLUserMapper.javapublicinterfaceUserMapper{UserselectById(intid);ListUserselectByName(Param(name)Stringname);voidinsert(Useruser);}UserMapper.xml?xml version1.0 encodingUTF-8?!DOCTYPEmapperPUBLIC-//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtdmappernamespacecom.example.mapper.UserMapperresultMapiduserMaptypecom.example.entity.Useridpropertyidcolumnid/resultpropertynamecolumnname/resultpropertyagecolumnage//resultMapselectidselectByIdresultMapuserMapSELECT * FROM user WHERE id #{id}/selectinsertidinsertuseGeneratedKeystruekeyPropertyidINSERT INTO user(name, age) VALUES(#{name}, #{age})/insert/mapper3.3 注解方式适合简单查询publicinterfaceUserMapper{Select(SELECT * FROM user WHERE id #{id})Results(iduserMap,value{Result(propertyid,columnid),Result(propertyname,columnname)})UserselectById(intid);Insert(INSERT INTO user(name, age) VALUES(#{name}, #{age}))Options(useGeneratedKeystrue,keyPropertyid)voidinsert(Useruser);}3.4 动态 SQL 示例XML 方式selectidselectByConditionresultTypecom.example.entity.UserSELECT * FROM userwhereiftestname ! null and name ! AND name LIKE CONCAT(%, #{name}, %)/ififtestminAge ! nullAND age #{minAge}/ififtestmaxAge ! nullAND agelt; #{maxAge}/if/where/select3.5 一级缓存陷阱多线程/异步场景ServicepublicclassUserService{AutowiredprivateUserMapperuserMapper;AsyncpublicCompletableFutureUserasyncGetUser(intid){// ⚠️ 注意异步方法会开启新事务不会复用原 SqlSession// 因此不会读到主线程的一级缓存可能触发多次 DB 查询returnCompletableFuture.completedFuture(userMapper.selectById(id));}}避坑一级缓存仅在同一 SqlSession通常同一事务内有效。跨线程、跨事务不会共享缓存。高并发场景下避免依赖一级缓存做一致性保证。3.6 MyBatis-Plus 实战2026 开发标配引入依赖dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.6/version/dependency实体类与 MapperDataTableName(user)publicclassUser{TableId(typeIdType.AUTO)privateIntegerid;privateStringname;privateIntegerage;}// 继承 BaseMapper自动拥有 CRUD 方法publicinterfaceUserMapperextendsBaseMapperUser{// 无需 XML可直接调用 insert、selectById 等}分页插件配置MyBatis-Plus 3.xConfigurationpublicclassMybatisPlusConfig{BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptorinterceptornewMybatisPlusInterceptor();interceptor.addInnerInterceptor(newPaginationInnerInterceptor(DbType.MYSQL));returninterceptor;}}使用示例ServicepublicclassUserService{AutowiredprivateUserMapperuserMapper;publicPageUserpage(intpageNum,intpageSize){PageUserpagenewPage(pageNum,pageSize);returnuserMapper.selectPage(page,null);}}对比原生 MyBatisMP 省去了 XML 和手写 SQL开发效率提升 50% 以上已成为 2026 年企业级开发的标准选择。四、避坑要点问题错误/误区后果正确做法SQL 注入${}拼接用户输入安全漏洞能用#{}就别用${}表名动态需白名单校验一级缓存误用异步方法或跨事务场景依赖缓存读到过时数据一级缓存仅限同一 SqlSession敏感数据主动清除二级缓存脏读关联表更新时未清空缓存数据不一致多表查询禁用二级缓存或使用cache-ref动态 SQL 拼接错误if条件后出现多余AND或ORSQL 语法错误使用where或trim标签自动处理结果映射失败列名与属性名不一致字段为 null开启map-underscore-to-camel-case或手动配置resultMap批量插入性能循环调用 insert频繁提交性能差使用foreach批量插入或 MyBatis-PlussaveBatchMyBatis-Plus 分页未配置分页插件内存分页性能问题配置MybatisPlusInterceptorXML 与注解混用同名方法同时存在冲突随机执行同一 Mapper 方法只选一种方式不要重复定义五、面试高频考点Q1MyBatis 中#{}和${}的区别#{}使用预编译?防止 SQL 注入${}直接拼接字符串有注入风险。表名、列名等动态场景只能用${}但需做白名单校验。Q2MyBatis 的一级缓存和二级缓存区别一级缓存 SqlSession 级别默认开启事务内复用二级缓存 Mapper 级别需配置多个 SqlSession 共享但多表查询可能脏读。生产环境二级缓存慎用。Q3MyBatis 如何实现结果映射通过resultType自动映射要求列名与属性一致或resultMap自定义映射支持复杂对象、关联查询。可开启驼峰命名转换map-underscore-to-camel-case。Q4MyBatis 动态 SQL 原理使用 OGNL 表达式解析if、where等标签在生成 SQL 前动态拼接最终构建成完整 SQL 字符串。Q5MyBatis 如何与 Spring Boot 整合MybatisAutoConfiguration自动装配类读取application.yml配置创建SqlSessionFactoryMapperScan扫描 Mapper 接口并生成代理 Bean注入到 Service 中。Q6MyBatis-Plus 相比原生 MyBatis 有哪些优势提供BaseMapper通用 CRUD、Lambda 查询、代码生成器、分页插件、乐观锁插件等大幅减少重复代码开发效率提升 50% 以上。Q7MyBatis 执行流程加载配置 → 创建SqlSessionFactory→ 打开SqlSession→ 获取 Mapper 代理 → 调用方法 →Executor执行 SQL → 参数处理 → 结果映射 → 返回。六、实战练习题代码题使用 MyBatis-Plus 实现用户分页查询年龄 ≥ 18并按年龄降序排序。 思路构建LambdaQueryWrapperUser调用userMapper.selectPage(page, wrapper)。分析某服务中使用二级缓存查询用户信息后另一服务修改了用户年龄再次查询时读到旧数据。为什么如何解决 思路二级缓存基于 Mapper namespace多表关联更新不会自动清空其他表的缓存。解决将关联表缓存在同一 namespace 下或禁用二级缓存或使用cache-ref。改错指出下面动态 SQL 的问题并改正。selectidsearchresultTypeUserSELECT * FROM user WHERE 11iftestname ! nullAND name #{name}/ififtestage ! nullAND age #{age}/if/select 问题无问题WHERE 11是常用写法可避免条件前多余的 AND。更优雅的方式是用where标签替代WHERE 11。️ 互动时间你在项目中踩过 MyBatis 的哪些坑欢迎在评论区吐槽我会挑选 3 个最典型的坑在下一篇源码篇中详细解答粉丝专属福利资料已打包在评论区回复“MyBatis”我会把《MyBatis 核心组件关系图与执行流程图》高清 PDF 发给你如果觉得这篇对你有帮助别忘了【点赞】【收藏】【评论】一键三连你的支持是我爆肝更新的动力 你的学习进度当前第86篇 / 共108篇 ·进阶篇数据库与持久层框架第83~90篇✅ 已完成基础篇44篇 第91~ 96篇Redis/MQ 第83~86篇 正在学第86篇⏳ 待学习第87~ 90篇 第97~108篇 完整目录 学习指南 | 订阅本专栏不错过每一篇 本专栏每篇都包含避坑表 面试高频考点 练习题。每天30分钟100天拿offer 下一篇文章预告《第87篇MyBatis源码阅读2026版》内容简介深入 MyBatis 源码剖析SqlSessionFactory构建过程、MapperProxy动态代理、Executor执行链路、StatementHandlerSQL 处理、结果映射原理以及 Spring 整合的底层细节。 学完这篇你将能自定义 MyBatis 插件理解框架设计精髓。福利提醒评论区留言“MyBatis”可领取《MyBatis 核心组件关系图与执行流程图》PDF。《Java 100 天进阶之路 | 从入门到上岗就业》每天一篇建议收藏 关注一起100天拿offer 点击关注我更新后第一时间收到推送