MyBatis高频面试题全解析含原理实战避坑MyBatis作为Java后端核心持久层框架是面试中必问的重点模块。本文整理了MyBatis从基础概念、核心原理到实战应用、性能优化的全维度高频面试题结合业务场景给出精准回答思路既覆盖入门级考点也包含中高级面试的深度问题。一、基础概念类入门必答1. 什么是MyBatis它与JDBC、Hibernate的核心区别是什么核心回答MyBatis是一款轻量级的半自动化ORM持久层框架通过XML/注解将SQL与Java代码解耦支持自定义SQL、存储过程和高级映射既保留了JDBC的灵活性又简化了数据库操作。特性JDBCMyBatisHibernateSQL控制手动编写繁琐易出错自定义编写灵活可控自动生成无需手动编写全自动化开发效率低需手动处理连接、结果集中简化连接/结果映射高全自动化ORM性能高无额外封装较高轻量封装接近JDBC较低自动SQL优化差重量级学习成本低中高需掌握HQL、缓存、关联映射适用场景简单小项目互联网高并发、需定制SQL的场景企业级应用、非高并发场景核心区别总结对比JDBCMyBatis封装了连接管理、结果集映射消除重复代码保留SQL编写灵活性对比HibernateMyBatis是“半自动化”需手动写SQL适合需要精细优化SQL的场景Hibernate是“全自动化”无需写SQL适合快速开发但SQL优化能力弱。2. MyBatis的核心组件有哪些各自的作用是什么核心回答MyBatis的核心组件围绕“SQL执行流程”设计核心组件及作用如下SqlSessionFactory会话工厂MyBatis的核心入口通过SqlSessionFactoryBuilder从配置文件/代码构建单例设计负责创建SqlSessionSqlSession会话对象封装了数据库操作CRUD是MyBatis与数据库交互的核心接口线程不安全需每次使用后关闭Mapper接口自定义的DAO接口MyBatis通过动态代理将接口与XML/注解中的SQL绑定无需编写实现类Executor执行器SqlSession的底层执行引擎负责SQL执行和缓存管理分为SimpleExecutor默认执行器每次执行新建StatementReuseExecutor复用Statement执行器BatchExecutor批量执行器StatementHandler处理StatementPreparedStatement/Statement的创建、参数设置、SQL执行ParameterHandler处理SQL参数的映射将Java参数转换为JDBC参数ResultSetHandler处理SQL结果集将JDBC结果集映射为Java对象ConfigurationMyBatis的全局配置对象存储所有配置信息数据源、映射器、插件等。3. MyBatis中#{ }和${ }的区别使用时需要注意什么核心回答两者都是参数占位符核心差异在于底层实现和安全特性特性#{ }${ }底层实现JDBC预编译PreparedStatement字符串直接拼接SQL注入防护支持参数作为占位符自动转义不支持直接拼接存在注入风险引号自动添加是字符串自动加单引号否需手动添加适用场景普通参数注入条件、字段值动态SQL拼接表名、排序字段使用注意事项优先使用#{ }覆盖99%的常规场景杜绝SQL注入仅在动态拼接SQL如动态表名、动态排序字段时使用${ }且必须对入参做严格合法性校验如限制排序字段只能是id、user_name等固定值示例!-- 安全普通参数注入 --SELECT * FROM t_user WHERE id #{id}!-- 谨慎使用动态表名需校验tableName的合法性 --SELECT * FROM ${tableName} WHERE id #{id}二、核心原理类中高级必答1. MyBatis的Mapper接口为什么不需要实现类底层动态代理原理是什么核心回答MyBatis通过JDK动态代理为Mapper接口生成代理实现类核心流程如下当调用SqlSession.getMapper(UserMapper.class)时MyBatis会为Mapper接口创建动态代理对象代理对象拦截接口方法调用获取方法对应的namespace methodName匹配Mapper.xml中的SQL封装方法入参为SQL参数通过Executor执行SQL处理结果集并映射为返回值返回处理后的结果给调用方。核心源码逻辑简化// MapperProxy是动态代理的核心类实现InvocationHandlerpublicclassMapperProxyTimplementsInvocationHandler{OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{// 1. 获取方法对应的MappedStatement封装SQL的对象StringstatementIdmapperInterface.getName().method.getName();MappedStatementmsconfiguration.getMappedStatement(statementId);// 2. 执行SQL并处理结果returnsqlSession.selectOne(statementId,args);}}总结Mapper接口仅作为“SQL映射标识”MyBatis通过动态代理将接口方法与SQL绑定无需手动编写实现类简化开发。2. MyBatis的一级缓存和二级缓存有什么区别如何使用核心回答MyBatis的缓存分为一级缓存本地缓存和二级缓存全局缓存核心作用是减少数据库查询次数提升性能特性一级缓存二级缓存作用域SqlSession会话级别Mapper命名空间级别生命周期随SqlSession创建/关闭而变化随SqlSessionFactory生命周期存在默认状态开启无法关闭关闭需手动开启数据共享仅当前SqlSession可见所有SqlSession共享存储介质内存内存/磁盘可自定义使用方式一级缓存无需配置默认开启。同一SqlSession中相同SQL查询会先从一级缓存获取若缓存未失效无更新/删除操作则不查数据库失效场景SqlSession关闭/提交/回滚、执行update/delete/insert操作、手动清除缓存二级缓存步骤1全局配置开启二级缓存MyBatis-config.xmlsettingssettingnamecacheEnabledvaluetrue/!-- 默认true可省略 --/settings步骤2在Mapper.xml中开启缓存mappernamespacecom.example.dao.UserMappercache/!-- 开启当前Mapper的二级缓存 --!-- 或自定义缓存配置 --cacheevictionLRUflushInterval60000size1024readOnlytrue//mapper步骤3实体类需实现Serializable接口缓存序列化需要。注意事项二级缓存适用于读多写少的场景写操作频繁时会导致缓存频繁失效反而降低性能多表关联查询时二级缓存易出现数据不一致如User和Order关联更新Order后User缓存未更新需谨慎使用。3. MyBatis的动态SQL有哪些标签各自的作用是什么核心回答动态SQL是MyBatis的核心特性通过标签实现SQL的灵活拼接解决手动拼接SQL的繁琐与易错问题核心标签及作用标签核心作用示例场景if单条件判断动态添加SQL片段根据入参是否非空添加查询条件where自动处理WHERE关键字和多余AND/OR结合使用避免无条件时出现WHERE 11set自动处理SET关键字和多余逗号动态更新非空字段剔除最后一个逗号choose多条件分支if-else多个条件中仅满足一个如按名称或ID查询foreach循环遍历集合批量查询IN条件、批量新增/删除trim自定义字符串截取替代/灵活处理前缀/后缀sql抽取公共SQL片段复用字段列表如SELECT id,user_name FROMinclude引用定义的公共片段配合实现SQL复用核心示例!-- ifwhere动态查询 --selectidselectUserresultTypeUserSELECT * FROM t_userwhereiftestuserName ! nullAND user_name LIKE #{userName}/ififtestage ! nullAND age #{age}/if/where/select!-- foreach批量删除 --deleteiddeleteByIdsDELETE FROM t_user WHERE id INforeachcollectionidsitemidopen(separator,close)#{id}/foreach/delete4. MyBatis如何解决字段名与实体类属性名不一致的问题核心回答有3种主流解决方案按推荐优先级排序开启驼峰命名自动映射推荐在MyBatis-config.xml中配置自动将数据库下划线命名如user_name映射为Java驼峰命名如userNamesettingssettingnamemapUnderscoreToCamelCasevaluetrue//settings使用resultMap手动映射适用于字段名与属性名差异较大的场景自定义字段与属性的映射关系resultMapiduserResultMaptypeUseridcolumnuser_idpropertyid/!-- 数据库user_id → 实体id --resultcolumnuser_namepropertyuserName/!-- user_name → userName --/resultMapselectidselectByIdresultMapuserResultMapSELECT user_id, user_name FROM t_user WHERE user_id #{id}/selectSQL别名在SQL中为字段添加别名匹配实体类属性名selectidselectByIdresultTypeUserSELECT user_id AS id, user_name AS userName FROM t_user WHERE user_id #{id}/select三、实战应用类高频业务场景1. MyBatis中updateByPrimaryKey和updateByPrimaryKeySelective的区别实际开发中选哪个核心回答两者均为按主键更新单条数据核心差异在于更新字段范围方法名更新字段范围风险点适用场景updateByPrimaryKey全字段更新空字段会覆盖数据库原有值单条全字段更新极少用updateByPrimaryKeySelective仅更新非空字段无空字段不更新单条部分字段更新日常主流实战建议优先使用updateByPrimaryKeySelective避免因入参空字段导致数据库数据丢失示例入参仅封装userName和id则仅更新user_name字段age、email等字段保持原有值。2. MyBatis如何实现分页有哪些方式核心回答主流有3种分页方式按推荐优先级排序MyBatis-Plus分页插件推荐基于MyBatis扩展配置简单支持物理分页// 配置分页插件BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptorinterceptornewMybatisPlusInterceptor();interceptor.addInnerInterceptor(newPaginationInnerInterceptor(DbType.MYSQL));returninterceptor;}// 使用PageUserpagenewPage(1,10);// 第1页每页10条userMapper.selectPage(page,null);手动编写分页SQL原生MyBatis基于数据库方言实现MySQL用LIMITOracle用ROWNUMselectidselectUserPageresultTypeUserSELECT * FROM t_user LIMIT #{offset}, #{size}/selectPageHelper分页插件第三方插件拦截SQL自动添加分页条件PageHelper.startPage(1,10);ListUseruserListuserMapper.selectAll();PageInfoUserpageInfonewPageInfo(userList);注意避免使用“内存分页”查询所有数据后在Java端分页数据量大时会导致内存溢出。3. MyBatis如何实现批量操作新增/更新/删除核心回答批量新增推荐insertidbatchInsertINSERT INTO t_user (user_name, age) VALUESforeachcollectionlistitemuserseparator,(#{user.userName}, #{user.age})/foreach/insert批量更新方式1拼接UPDATEMySQL支持updateidbatchUpdateUPDATE t_user SET user_name CASE idforeachcollectionlistitemuserWHEN #{user.id} THEN #{user.userName}/foreachEND WHERE id INforeachcollectionlistitemuseropen(separator,close)#{user.id}/foreach/update方式2通过ExecutorType.BATCH批量执行SqlSessionsqlSessionsqlSessionFactory.openSession(ExecutorType.BATCH);UserMappermappersqlSession.getMapper(UserMapper.class);for(Useruser:list){mapper.updateByPrimaryKeySelective(user);}sqlSession.commit();sqlSession.close();批量删除deleteidbatchDeleteDELETE FROM t_user WHERE id INforeachcollectionidsitemidopen(separator,close)#{id}/foreach/delete实战注意批量操作需控制数据量建议每批次1000条以内避免单条SQL过长导致数据库锁表。四、性能优化类高级面试1. MyBatis的性能优化有哪些常用手段核心回答从“SQL、缓存、执行、配置”4个维度优化SQL优化避免SELECT *只查询需要的字段为查询条件字段建立索引减少关联查询拆分复杂SQL批量操作替代单条循环操作缓存优化合理使用一级缓存默认开启读多写少场景开启二级缓存避免多表关联查询使用二级缓存防止数据不一致执行优化使用ReuseExecutor/BatchExecutor提升执行效率关闭不必要的日志如DEBUG级别使用连接池如Druid管理数据库连接避免频繁创建/关闭连接配置优化开启驼峰命名自动映射减少resultMap配置配置延迟加载association/collection的lazyLoad减少不必要的关联查询禁用二级缓存的脏读保护按需提升缓存命中率。2. MyBatis的延迟加载是什么如何实现核心回答延迟加载懒加载指关联查询时先加载主表数据关联表数据在真正使用时才查询核心作用是减少不必要的数据库查询提升性能。实现方式全局开启延迟加载MyBatis-config.xmlsettingssettingnamelazyLoadingEnabledvaluetrue/!-- 开启延迟加载 --settingnameaggressiveLazyLoadingvaluefalse/!-- 关闭积极懒加载 --/setting在resultMap中配置关联查询的延迟加载resultMapiduserWithOrderMaptypeUseridcolumnidpropertyid/!-- 一对多关联延迟加载订单数据 --collectionpropertyorderListjavaTypeListofTypeOrderselectcom.example.dao.OrderMapper.selectByUserIdcolumnidfetchTypelazy//resultMap注意延迟加载基于动态代理实现仅在调用关联属性如user.getOrderList()时才执行关联查询若关联数据必用建议关闭延迟加载避免多次查询数据库。五、总结关键点回顾MyBatis核心优势是SQL灵活可控对比JDBC简化开发对比Hibernate更适合高并发、需定制SQL的场景核心原理围绕动态代理Mapper接口、执行器Executor、缓存一级/二级展开是中高级面试的核心考点实战中优先使用#{ }、updateByPrimaryKeySelective、驼峰命名映射避免SQL注入和数据丢失性能优化需结合“SQL、缓存、执行、配置”多维度核心是减少数据库查询次数和优化SQL执行效率。