1. MyBatis面试题解析与实战经验作为Java生态中最受欢迎的ORM框架之一MyBatis在技术面试中出现的频率极高。根据笔者多年面试经验面试官通常会从基础用法、原理机制、性能优化等维度进行考察。以下是笔者在实际面试中遇到的典型问题及深度解析。1.1 基础概念与核心组件问题1请描述MyBatis的核心组件及其交互流程MyBatis的核心架构包含以下关键组件SqlSessionFactory通过Builder模式创建是生成SqlSession的工厂类。一个数据库对应一个SqlSessionFactory实例。SqlSession代表一次数据库会话线程不安全。通过它执行CRUD操作最佳实践是在方法作用域内使用。ExecutorSQL语句的实际执行器包含三种类型SimpleExecutor默认执行器每次执行都会创建新的StatementReuseExecutor复用预处理语句BatchExecutor批量操作优化MappedStatement封装了SQL映射信息参数映射、结果映射、SQL语句等TypeHandler完成JDBC类型与Java类型的双向转换典型交互流程示例// 初始化阶段 String resource mybatis-config.xml; InputStream inputStream Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream); // 运行时阶段 try (SqlSession session sqlSessionFactory.openSession()) { UserMapper mapper session.getMapper(UserMapper.class); User user mapper.selectById(1L); }问题2#{}和${}的区别是什么这是MyBatis面试必问题目两者的核心差异在于#{}使用预编译处理会替换为?占位符能有效防止SQL注入${}直接文本替换存在注入风险但适合动态表名、列名场景安全提示生产环境必须对${}的内容进行严格校验或使用白名单机制1.2 动态SQL与高级特性问题3MyBatis动态SQL有哪些实现方式MyBatis提供多种动态SQL构建方式XML方式select idfindUsers resultTypeUser SELECT * FROM users where if testname ! null AND name #{name} /if choose when testrole admin AND level 10 /when otherwise AND level 5 /otherwise /choose /where /select注解方式3.5版本SelectProvider(type UserSqlBuilder.class, method buildGetUserByName) User getUserByName(String name); class UserSqlBuilder { public String buildGetUserByName(String name) { return new SQL(){{ SELECT(*); FROM(users); if (name ! null) { WHERE(name #{name}); } }}.toString(); } }Lambda方式MyBatis-Plus特性QueryWrapperUser query new QueryWrapper(); query.lambda() .eq(User::getName, 张三) .gt(User::getAge, 18);问题4如何实现分页查询MyBatis分页方案对比方案原理优点缺点内存分页使用RowBounds实现简单数据量大时性能差物理分页手动拼limit性能好SQL方言差异PageHelper拦截器改写SQL使用方便依赖第三方库MyBatis-Plus内置分页插件功能全面需要整合推荐实现使用PageHelper// 开启分页 PageHelper.startPage(1, 10); ListUser users userMapper.selectAll(); // 获取分页信息 PageInfoUser pageInfo new PageInfo(users);1.3 缓存机制与性能优化问题5MyBatis的缓存机制是怎样的MyBatis采用二级缓存设计一级缓存本地缓存作用域SqlSession级别默认开启通过localCacheScope参数可配置失效场景执行update操作、手动clearCache、会话关闭二级缓存全局缓存作用域Mapper级别需要显式配置cache/标签注意事项实体类必须实现Serializable事务提交后才生效集群环境需要配合Redis等集中式缓存缓存执行顺序二级缓存 → 一级缓存 → 数据库问题6如何优化MyBatis批量插入性能批量操作性能优化方案使用BatchExecutorSqlSession session sqlSessionFactory.openSession(ExecutorType.BATCH); try { UserMapper mapper session.getMapper(UserMapper.class); for (User user : userList) { mapper.insert(user); } session.commit(); } finally { session.close(); }foreach批量插入insert idbatchInsert INSERT INTO users(name,age) VALUES foreach collectionlist itemitem separator, (#{item.name}, #{item.age}) /foreach /insertMyBatis-Plus的saveBatch底层采用方案2userService.saveBatch(userList, 1000); // 每批1000条性能对比测试数据量1万条单条循环12.5秒BatchExecutor3.2秒foreach批量1.8秒1.4 插件开发与原理剖析问题7如何自定义MyBatis插件插件开发步骤示例实现SQL执行时间监控定义拦截器Intercepts({ Signature(type Executor.class, method query, args {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), Signature(type Executor.class, method update, args {MappedStatement.class, Object.class}) }) public class SqlCostInterceptor implements Interceptor { Override public Object intercept(Invocation invocation) throws Throwable { long start System.currentTimeMillis(); try { return invocation.proceed(); } finally { long cost System.currentTimeMillis() - start; System.out.println(SQL执行耗时 cost ms); } } }注册插件plugins plugin interceptorcom.example.SqlCostInterceptor property namethreshold value500/ /plugin /plugins问题8MyBatis的Mapper接口如何被实例化这是考察MyBatis核心原理的典型问题关键过程如下通过JDK动态代理生成MapperProxy代理对象调用方法时代理对象会根据方法名和参数解析对应的MappedStatement创建MapperMethod对象封装SQL执行逻辑通过SqlSession执行数据库操作最终由DefaultSqlSession将操作委托给Executor核心源码片段// MapperProxy.java public Object invoke(Object proxy, Method method, Object[] args) { // 处理Object方法 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } // 转换为MapperMethod执行 return cachedMapperMethod(method).execute(sqlSession, args); }2. 高频问题与避坑指南2.1 常见异常处理问题9遇到TooManyResultsException异常如何解决这个异常通常表明查询返回了多行记录但试图映射到单个对象。解决方案检查SQL是否缺少唯一性条件确认是否应该使用List接收结果使用ResultType指定返回类型添加LIMIT 1限制适用场景问题10如何解决N1查询问题典型表现查询主表后循环查询关联表。解决方案对比方案实现方式适用场景联合查询通过JOIN一次性获取关联数据量小嵌套结果使用collection映射中等数据量批量查询配合BatchSize注解大数据量延迟加载设置lazyLoadingEnabledtrue按需加载示例配置settings setting namelazyLoadingEnabled valuetrue/ setting nameaggressiveLazyLoading valuefalse/ /settings2.2 最佳实践建议XML管理规范保持SQL的缩进格式一致复杂查询添加注释说明业务逻辑为每个Mapper XML添加DOCTYPE声明!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd参数处理技巧使用Param注解明确参数名复杂参数建议封装为DTO日期处理DateTimeFormat(pattern yyyy-MM-dd) private Date startDate;结果映射优化优先使用resultMap避免字段歧义使用association处理一对一关系启用自动映射setting nameautoMappingBehavior valueFULL/3. MyBatis与Spring整合要点3.1 整合配置关键点问题11Spring Boot中如何配置MyBatis标准配置示例mybatis: configuration: map-underscore-to-camel-case: true # 自动驼峰转换 default-fetch-size: 100 default-statement-timeout: 30 mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.model问题12如何实现多数据源配置多数据源实现步骤定义多个DataSource Bean为每个数据源配置独立的SqlSessionFactory使用MapperScan指定各自的扫描路径事务管理需使用Transactional(transactionManager指定TM)3.2 事务管理实践问题13MyBatis如何与Spring事务集成集成原理Spring通过DataSourceTransactionManager管理事务MyBatis的SqlSession生命周期由Spring控制事务传播行为遵循Spring规范注意事项避免在Service层捕获异常不抛出批量操作建议使用编程式事务注意事务超时设置Transactional(timeout 30) public void batchProcess() {...}4. MyBatis-Plus进阶特性4.1 核心功能对比问题14MyBatis-Plus相比原生MyBatis有哪些优势功能对比表特性MyBatisMyBatis-PlusCRUD操作需手动编写内置通用Mapper条件构造需写XMLLambda表达式分页支持需插件原生支持代码生成无内置生成器乐观锁手动实现Version注解4.2 实际应用示例问题15如何使用LambdaQueryWrapper类型安全的查询示例ListUser users userMapper.selectList( new LambdaQueryWrapperUser() .eq(User::getDeptId, 2) .between(User::getAge, 20, 30) .likeRight(User::getName, 张) .orderByDesc(User::getCreateTime) );问题16如何实现逻辑删除配置步骤实体类添加注解TableLogic private Integer deleted;全局配置mybatis-plus: global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 05. 源码级面试题解析5.1 架构设计思想问题17MyBatis如何实现插件机制实现原理基于责任链模式通过InterceptorChain维护拦截器列表使用JDK动态代理生成代理对象支持拦截的四大对象ExecutorStatementHandlerParameterHandlerResultSetHandler5.2 性能优化深度问题18如何设计百万级数据导出高效导出方案采用流式查询Select(SELECT * FROM large_table) Options(resultSetType FORWARD_ONLY, fetchSize 1000) void streamQuery(ResultHandlerUser handler);分片处理// 按ID范围分片 for (long i 0; i maxId; i pageSize) { ListUser segment mapper.selectSegment(i, i pageSize); processSegment(segment); }配合MyBatis的Cursor接口try (CursorUser cursor mapper.scanAll()) { cursor.forEach(user - { // 处理单条记录 }); }6. 最新特性与趋势6.1 MyBatis 3.5新特性注解动态SQL支持在注解中使用SelectProvider等实现动态SQLKotlin DSL支持提供更友好的Kotlin集成增强的类型处理器支持Java8日期API等新特性6.2 云原生适配问题19MyBatis如何适应云原生环境适配方案配合HikariCP实现连接池优化使用Seata处理分布式事务集成Micrometer实现指标监控配置建议mybatis: configuration: cache-enabled: false # 禁用二级缓存 local-cache-scope: statement # 缩小一级缓存范围7. 面试准备建议知识体系构建掌握从SQL执行到结果映射的完整流程理解插件机制和动态代理的应用熟悉与Spring的整合原理实战准备准备2-3个复杂SQL的优化案例总结遇到的典型异常及解决方案对比MyBatis与其他ORM框架的优劣深度问题准备1-2个源码层面的分析问题思考MyBatis的设计哲学了解社区最新动态和发展方向在面试过程中建议采用STAR法则Situation-Task-Action-Result来组织回答结合具体业务场景说明技术选型和优化策略。对于原理性问题可适当画出核心类图或序列图辅助说明。