面试官问:MyBatis面试专题(含源码+MP)?一张图+点餐比喻,彻底拿下这道必考题(附图解+比喻+避坑指南)

发布时间:2026/7/21 9:42:27
面试官问:MyBatis面试专题(含源码+MP)?一张图+点餐比喻,彻底拿下这道必考题(附图解+比喻+避坑指南) 面试官问MyBatis面试专题含源码MP一张图点餐比喻彻底拿下这道必考题附图解比喻避坑指南预计阅读15分钟 你是不是也这样能写出MyBatis的增删改查但面试官一追问“工作原理是什么”“Mapper接口为什么没有实现类也能执行”就答不上来了今天一张图 一个点餐故事 源码级解析 8道追问彻底拿下这道题。摘要MyBatis是Java生态中最主流的半ORM框架通过封装JDBC、支持自定义SQL和动态代理成为企业级开发的标配。本文系统梳理MyBatis核心面试考点从工作原理、#{}与${}区别、Mapper动态代理源码、一二级缓存机制到MyBatis-Plus的增强特性BaseMapper、条件构造器、分页插件、逻辑删除全面覆盖基础概念到源码深度。一句话MyBatis是“手写SQL的灵活框架”MP是“MyBatis的效率外挂”。我是折哥《Java 85题图解版》系列连载中已更新33题建议收藏本系列。每周2-3篇85题通关路线一键追完。点击关注第一时间收到每篇新题推送。上一篇面试官问SQL优化与执行计划分析EXPLAIN下一篇面试官问线程生命周期与状态转换是什么全部85题点击查看总目录关注专栏追更不迷路一句话总结MyBatis是“手写SQL的灵活框架”MP是“MyBatis的效率外挂”。MyBatis是什么半ORM框架封装JDBC手写SQL → 像点餐系统你只需要看菜单点菜写SQL传菜员MyBatis自动把菜送到后厨执行。工作原理配置加载 → SqlSessionFactory → Mapper代理 → Executor执行 → 结果映射 → 像点餐系统从菜谱→中央厨房→传菜员→厨师→上菜一气呵成。**#{} vsKaTeX parse error: Expected EOF, got # at position 7: {}** #̲{}预编译防注入{}字符串替换 → 像点餐时用标准菜单编号#{}安全下单还是直接喊菜名${}让后厨自己去理解后者有风险。背诵口诀MyBatis 封装JDBC 手写SQL 动态代理MP 只做增强不做改变BaseMapper提供17个CRUD方法。核心设计理念SQL与代码分离 动态代理 灵活映射——不绑架你的SQL但帮你省掉JDBC的模板代码。 面试还原面试官MyBatis是什么它的工作原理是什么#{}和${}有什么区别Mapper接口为什么没有实现类也能执行这是Java后端面试中框架层最核心的考点直接进入正题。 一图看懂MyBatis核心架构全貌 生活比喻点餐系统MyBatis 餐厅后厨Configuration 菜谱记录每道菜怎么做SqlSessionFactory 中央厨房统一管理菜谱和厨师SqlSession 一次用餐会话开一个包间一个厨师服务Mapper接口 菜单客人只看到菜名不用管怎么做MapperProxy动态代理 传菜员客人点菜传菜员通知后厨做Executor 厨师真正动手做菜StatementHandler 备菜员准备食材、调料ResultSetHandler 摆盘员把做好的菜摆成客人想要的样子为什么Mapper接口不需要实现类客人点菜时只需要看菜单Mapper接口上的菜名不需要知道后厨怎么做的。传菜员MapperProxy会自动把菜名翻译成后厨指令。你只需要定义接口方法MyBatis在运行时动态生成“实现类”代理对象。 核心对比表面试速查版MyBatis vs JDBC vs Hibernate维度JDBCMyBatisHibernateSQL控制手写手写XML/注解自动生成HQLORM类型无半自动全自动学习成本高中高灵活度最高高低性能调优手写优化手写SQL优化需熟悉Hibernate机制适用场景底层框架企业级CRUD/复杂查询标准CRUD 核心面试考点深度解析考点1MyBatis工作原理高频⭐⭐⭐⭐⭐完整流程加载配置读取mybatis-config.xml和Mapper.xml解析成Configuration对象创建SqlSessionFactory通过SqlSessionFactoryBuilder构建一个应用只有一个获取SqlSessionsqlSessionFactory.openSession()每次请求创建一个获取Mapper代理session.getMapper(UserMapper.class)→JDK动态代理生成MapperProxy执行SQL调用Mapper方法 →MapperProxy.invoke()→ 从Configuration获取MappedStatementExecutor执行CachingExecutor→ 查一级/二级缓存 → 调用StatementHandler参数填充ParameterHandler将Java参数设置到PreparedStatement结果映射ResultSetHandler将ResultSet映射为Java对象面试回答要点能说出“Configuration → SqlSessionFactory → SqlSession → MapperProxy → Executor → StatementHandler → ResultSetHandler”这条完整链路。考点2#{} vs ${}最高频⭐⭐⭐⭐⭐对比项#{}${}底层处理预编译占位符→?纯字符串替换SQL注入✅防注入❌存在风险适用场景参数值传递动态表名/列名推荐度优先使用谨慎使用需白名单为什么#{}能防注入MyBatis先把SQL骨架发给数据库预编译参数值通过占位符独立传递数据库不会把参数当SQL逻辑解析。即使传入 OR 11也只是作为字符串值处理。考点3Mapper动态代理源码区分中高级⭐⭐⭐⭐⭐核心问题Mapper接口没有实现类为什么能执行答案MyBatis使用JDK动态代理为Mapper接口生成了实现类。// MyBatis源码核心逻辑简化publicclassMapperProxyTimplementsInvocationHandler{privatefinalSqlSessionsqlSession;privatefinalClassTmapperInterface;OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args){// 1. 从Configuration中查找对应的MappedStatementMappedStatementmsconfiguration.getMappedStatement(mapperInterface.getName().method.getName());// 2. 执行SQLreturnsqlSession.selectOne(ms.getId(),args);}}// 获取Mapper时创建代理publicTTgetMapper(ClassTtype){return(T)Proxy.newProxyInstance(type.getClassLoader(),newClass[]{type},newMapperProxy(sqlSession,type));}代理对象被缓存同一个Mapper接口在同一个SqlSession中多次getMapper()返回的是同一个代理对象实例。考点4MyBatis缓存机制高频⭐⭐⭐⭐⭐一级缓存SqlSession级别默认开启同一个SqlSession内重复查询返回缓存结果执行增删改会清空一级缓存Spring事务中多个Mapper共用同一个SqlSession二级缓存Mapper级别需手动配置通过cache/标签启用跨SqlSession共享支持LRU、FIFO等淘汰策略分布式环境下数据不一致生产环境建议禁用二级缓存面试坑点多表关联更新不会清空其他表的缓存 → 脏读。考点5MyBatis插件Interceptor原理加分项⭐⭐⭐⭐MyBatis插件拦截四大组件组件作用ExecutorSQL执行器拦截增删改查StatementHandlerSQL处理器拦截SQL构建ParameterHandler参数处理器拦截参数设置ResultSetHandler结果处理器拦截结果映射原理InterceptorChain存储所有插件创建四大组件时执行pluginAll()循环调用每个拦截器的plugin方法生成多层代理形成责任链。PageHelper、MP分页插件均基于此机制。 MyBatis-Plus专题面试必考MP是什么MyBatis-PlusMP是MyBatis的增强工具遵循**“只做增强不做改变”**原则。MP完全兼容MyBatis所有功能不修改MyBatis底层源码。核心价值BaseMapper提供17个现成CRUD方法告别手写单表SQL。// 使用MP后Mapper接口只需要继承BaseMapperMapperpublicinterfaceUserMapperextendsBaseMapperUser{// 17个CRUD方法全都有了无需写任何SQL}// 支持的方法insert、deleteById、updateById、selectById、selectList、selectPage...MP核心组件组件作用BaseMapper提供17个通用CRUD方法IService/ServiceImplService层通用封装支持批量操作Wrapper条件构造器构建动态SQL条件分页插件自动拼接分页SQL支持多数据库逻辑删除TableLogic将DELETE改为UPDATE乐观锁Version字段更新时检查版本号代码生成器自动生成Entity/Mapper/Service/ControllerWrapper条件构造器// QueryWrapper字段名用字符串易出错QueryWrapperUserwrappernewQueryWrapper();wrapper.eq(name,张三).ge(age,18).orderByDesc(create_time);// LambdaQueryWrapper用Lambda表达式类型安全推荐LambdaQueryWrapperUserwrappernewLambdaQueryWrapper();wrapper.eq(User::getName,张三).ge(User::getAge,18).orderByDesc(User::getCreateTime);MP分页插件与Count优化// 分页查询PageUserpagenewPage(pageNum,pageSize);// 复杂联查时关闭自动Count手动写轻量Count SQLpage.setSearchCount(false);LongtotaluserMapper.selectCount(wrapper);page.setTotal(total);ListUserlistuserMapper.selectPage(page,wrapper);MP逻辑删除原理TableLogic标记字段删除变为UPDATE查询自动过滤。底层通过LogicSqlInjector改写SQL。 高频面试追问8道大厂真题追问1SqlSession是线程安全的吗为什么回答要点不安全。每个SqlSession对应一个数据库连接多个线程共享会导致连接混乱。详细回答SqlSession不是线程安全的。每个SqlSession对应一个数据库连接内部持有Connection对象。多线程共享会导致连接被多个线程同时使用引发数据混乱。正确做法是每个线程使用自己的SqlSession通常通过Spring的SqlSessionTemplate管理——它保证每个线程绑定一个SqlSession。追问2MyBatis用了哪些设计模式回答要点至少说出4-5个。详细回答工厂模式SqlSessionFactory创建SqlSession代理模式MapperProxy动态代理Mapper接口建造者模式SqlSessionFactoryBuilder构建SqlSessionFactory模板方法模式BaseExecutor定义执行骨架子类实现具体逻辑装饰器模式CachingExecutor装饰Executor添加缓存功能责任链模式InterceptorChain形成插件拦截链追问3MyBatis懒加载原理是什么回答要点通过Javassist字节码代理实现。详细回答MyBatis懒加载通过Javassist动态字节码技术实现——运行时动态生成实体类的子类代理对象在关联属性的getter方法中植入拦截逻辑。当调用getter时触发拦截器自动执行关联查询并回填数据。JDK动态代理为什么不行JDK动态代理必须基于接口而实体类通常没有接口。常见坑实体类重写toString方法会触发所有getter调用导致懒加载失效。追问4嵌套查询和嵌套结果有什么区别回答要点嵌套查询多条SQL懒加载嵌套结果单条SQLJOIN。详细回答嵌套查询Nested Query先查主表再循环查关联表N1条SQL支持懒加载SQL简单但性能差嵌套结果Nested Result一次JOIN查询获取所有数据1条SQL不支持懒加载SQL复杂但性能好追问5为什么生产环境建议禁用二级缓存回答要点分布式环境下数据不一致。详细回答MyBatis二级缓存基于本地内存多节点部署时数据不一致。关联表更新也不会自动清空其他表的缓存。生产环境标准做法直接禁用MyBatis二级缓存改用Redis等分布式缓存。追问6MyBatis和MyBatis-Plus能共存吗回答要点可以MP完全兼容MyBatis。详细回答可以共存。MP只是MyBatis的增强不修改MyBatis底层。可以在同一个项目中同时使用单表CRUD用MP的BaseMapper复杂SQL用MyBatis的XML或注解MP的TableName、TableField注解增强字段映射追问7MP的分页插件原理是什么回答要点通过MyBatis插件Interceptor拦截Executor.query()。详细回答MP分页插件实现Interceptor接口拦截Executor的query方法。执行时先解析SQL在SQL后面拼接LIMIT分页语句再执行查询并封装成Page对象返回。追问8MP的逻辑删除底层怎么实现的回答要点通过LogicSqlInjector改写SQL。详细回答MP在TableLogic标记的字段上自动将DELETE语句改写为UPDATE语句查询时自动过滤已删除数据。底层通过LogicSqlInjector在SQL注入阶段替换原SQL。 避坑指南序号错误做法正确做法后果1在循环中使用${}使用#{}SQL注入风险2生产环境开启二级缓存禁用改用Redis分布式脏读3实体类重写toString避免调用关联字段的getter懒加载失效4多个Mapper共享SqlSession每个线程独立SqlSession线程安全问题5手写单表CRUD用MP的BaseMapper代码冗余6复杂查询用MP自动生成手写优化SQL性能问题 可运行验证代码// MyBatis核心配置 // mybatis-config.xmlconfigurationsettingssetting namelazyLoadingEnabledvaluetrue/!--开启懒加载--setting nameaggressiveLazyLoadingvaluefalse/!--按需加载--setting namecacheEnabledvaluefalse/!--生产环境禁用二级缓存--/settings/configuration// MyBatis-Plus Mapper MapperpublicinterfaceUserMapperextendsBaseMapperUser{// 自定义复杂查询Select(SELECT * FROM user WHERE age #{age})ListUserselectByAge(Param(age)intage);}// Service层 ServicepublicclassUserServiceextendsServiceImplUserMapper,User{publicPageUserpageQuery(intpageNum,intpageSize,Stringname){LambdaQueryWrapperUserwrappernewLambdaQueryWrapper();wrapper.like(StringUtils.hasText(name),User::getName,name).orderByDesc(User::getCreateTime);returnthis.page(newPage(pageNum,pageSize),wrapper);}}❓ 评论区挑战问题以下关于MyBatis的说法哪一个是错误的// 场景使用MyBatis查询用户MapperpublicinterfaceUserMapper{Select(SELECT * FROM user WHERE name #{name})UserfindByName(Stringname);}A. MyBatis通过JDK动态代理为Mapper接口生成实现类B. #{}使用预编译占位符可以防止SQL注入C. 一级缓存是SqlSession级别默认开启D. 二级缓存生产环境推荐开启能显著提升性能 欢迎在评论区写出你的答案和理由我会在下一篇文章发布后更新本文公布答案及错误选项逐项解析。✅ 答案公布正确答案D. 二级缓存生产环境推荐开启能显著提升性能解析二级缓存基于本地内存多节点部署会数据不一致关联表更新不会自动清空其他表的缓存易导致脏读生产环境标准做法禁用MyBatis二级缓存改用Redis选项A正确MapperProxy基于JDK动态代理选项B正确#{}是预编译占位符选项C正确一级缓存默认开启错误选项逐项解析AJDK动态代理正确。MyBatis用JDK动态代理生成MapperProxyB#{}防注入正确。预编译让参数不被解析为SQL逻辑C一级缓存默认开启正确。SqlSession级别缓存D二级缓存生产推荐开启错误。分布式脏读风险应用Redis 总结层级核心内容关键点工作原理配置加载→SqlSessionFactory→Mapper代理→Executor执行完整链路#{} vs ${}#{}预编译防注入${}字符串替换动态表名用${}动态代理JDK动态代理生成MapperProxy代理对象缓存复用缓存机制一级SqlSession级二级Mapper级生产禁用二级缓存懒加载Javassist字节码代理toString导致失效MyBatis-PlusBaseMapper Wrapper 分页插件只做增强不做改变面试官最看重的三个点工作原理完整链路能画出从配置加载到结果映射的全流程#{} vs ${}深度能说清预编译原理和防注入机制动态代理源码能讲清楚MapperProxy的invoke逻辑 系列导航上一篇面试官问SQL优化与执行计划分析EXPLAIN下一篇面试官问线程生命周期与状态转换是什么全部85题目录点击查看关注专栏每周2-3篇一键追更搭配学习效果更佳本篇图解帮你快速建立知识画面记忆如果想深入理解源码实现和实战避坑细节可以配合姊妹系列《Java 100天进阶之路》对应章节一起学从零基础到上岗就业108篇完整学习地图每篇标配生活类比 可运行代码 避坑表 面试高频题 练习题不背八股文真正讲透“为什么”。 《Java 100天进阶之路》完整目录导航学习建议图解系列负责“快速建立知识图谱”进阶系列负责“深入理解原理”两个系列搭配使用面试备考效率翻倍。你在实际项目中使用过MyBatis-Plus吗遇到过二级缓存导致的脏读问题吗欢迎评论区分享你的故事