1. MyBatis面试核心知识点解析
作为Java生态中最受欢迎的ORM框架之一,MyBatis在技术面试中的出现频率居高不下。根据近三年一线互联网企业的面试统计,MyBatis相关问题的考察占比达到Java技术栈面试的35%以上。本专题将系统梳理高频考点,结合框架设计原理和实际开发经验,帮助开发者建立完整的知识体系。
1.1 基础架构与核心组件
MyBatis的核心架构可分为接口层、数据处理层和基础支撑层。面试中最常被问及的SqlSessionFactory构建过程,实际上采用了Builder模式,通过XMLConfigBuilder解析mybatis-config.xml文件,最终生成包含所有配置信息的Configuration对象。
java复制// 典型初始化代码示例
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
关键点:SqlSessionFactory是线程安全的,通常作为单例存在于应用中。而SqlSession是线程不安全的,每次数据库操作都应该创建新的实例并及时关闭。
1.2 动态SQL实现机制
动态SQL是MyBatis的特色功能,面试官常会要求对比#{}和${}的区别:
#{}采用预编译处理,能有效防止SQL注入${}直接进行字符串替换,适用于动态表名、列名场景
xml复制<!-- 动态SQL典型示例 -->
<select id="findUsers" parameterType="map" resultType="User">
SELECT * FROM users
<where>
<if test="name != null">AND name = #{name}</if>
<if test="age != null">AND age = #{age}</if>
<choose>
<when test="orderBy == 'name'">ORDER BY name</when>
<otherwise>ORDER BY id</otherwise>
</choose>
</where>
</select>
在安全扫描报SQL注入漏洞的场景下,需要特别注意:
- 避免在用户输入可控的场景使用
${} - 必须使用时需严格校验输入内容
- 考虑使用MyBatis的
@Param注解明确参数绑定
2. 高级特性与性能优化
2.1 缓存机制深度剖析
MyBatis提供两级缓存设计,常成为面试中的难点:
- 一级缓存(本地缓存):SqlSession级别,默认开启
- 生命周期与SqlSession一致
- 执行update操作会自动清空
- 二级缓存(全局缓存):Mapper级别,需要显式配置
- 跨SqlSession共享
- 需要实体类实现Serializable接口
xml复制<!-- 二级缓存配置示例 -->
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
缓存使用不当会导致脏读问题,典型解决方案:
- 对实时性要求高的查询关闭缓存
- 在涉及多表关联的场景谨慎使用
- 通过
<select flushCache="true">强制刷新
2.2 插件开发与原理
MyBatis的插件机制基于责任链模式实现,通过拦截器可以扩展框架功能。面试中常要求手写简单插件:
java复制@Intercepts({
@Signature(type= Executor.class, method="query",
args={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class QueryTimeInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
long end = System.currentTimeMillis();
System.out.println("执行耗时:" + (end - start) + "ms");
return result;
}
}
插件开发要点:
- 使用
@Intercepts注解定义拦截目标 - 通过
invocation.proceed()继续执行责任链 - 在配置文件中注册插件
3. 实战问题排查与解决方案
3.1 典型异常处理
问题1:Flowable-ui覆盖MyBatis配置
当集成工作流引擎时,可能出现配置冲突。解决方案:
- 检查依赖树排除冲突jar包
- 明确指定MyBatis配置位置
- 使用
@MapperScan注解明确扫描路径
问题2:TypeHandler注册失效
处理特殊类型转换时,确保:
- 在mybatis-config.xml中正确注册
- 实现完整的TypeHandler接口
- 在字段映射中指定jdbcType和javaType
3.2 性能优化方案
- 批量操作优化:
java复制// 批量插入最佳实践
try(SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
UserMapper mapper = session.getMapper(UserMapper.class);
for(int i=0; i<1000; i++){
mapper.insert(new User(...));
if(i % 200 == 0){
session.flushStatements();
}
}
session.commit();
}
- 复杂查询优化:
- 使用
<resultMap>替代自动映射 - 合理配置懒加载
- 对大数据量查询考虑分页插件
4. 高频面试题精讲
4.1 基础概念类
Q1:MyBatis与Hibernate主要区别?
- MyBatis:SQL友好、灵活性高、学习曲线平缓
- Hibernate:全自动ORM、HQL查询、缓存机制更完善
- 选择依据:项目复杂度、团队熟悉度、性能要求
Q2:Mapper接口工作原理?
通过JDK动态代理生成实现类:
- 解析Mapper.xml中的SQL语句
- 创建MapperProxy代理对象
- 方法调用时转入
SqlSession执行
4.2 源码分析类
Q3:SQL执行过程解析
SqlSession获取Mapper代理- 通过
MappedStatement获取BoundSql StatementHandler创建预处理语句ParameterHandler设置参数ResultSetHandler处理结果集
Q4:插件拦截原理
基于责任链模式:
- 创建目标对象时检查拦截器
- 通过
Plugin.wrap()生成代理链 - 调用时依次执行拦截逻辑
5. 最新技术动态与扩展
5.1 MyBatis-Plus进阶用法
存在则跳过实现方案:
java复制// 使用saveOrUpdate方法
userService.saveOrUpdate(user, Wrappers.<User>lambdaUpdate()
.eq(User::getUsername, user.getUsername()));
动态字段处理技巧:
- 使用
@TableField(update="now()")自动填充 - 通过
Wrapper实现条件更新 - 结合
MetaObjectHandler实现自动填充
5.2 微服务架构下的实践
在Spring Cloud环境中:
- 配置中心管理MyBatis配置
- 分布式ID生成策略
- 多数据源动态路由方案
对于复杂查询场景,建议:
- 使用Redis缓存热点数据
- 考虑CQRS模式分离读写
- 对JOIN操作进行适当拆解
