MyBatis 面试高频题及答案详解(附实战案例)
MyBatis 作为 Java 开发中广泛使用的持久层框架,因其灵活的 SQL 控制能力和高效的性能优化策略而备受青睐。本文基于实际面试场景,整理了 MyBatis 的高频面试题及详细解析,涵盖核心概念、动态 SQL、缓存机制、高级功能和实际应用案例,帮助开发者全面掌握 MyBatis 的核心知识点。
一、MyBatis 基础概念与核心功能
1. MyBatis 的核心配置文件是什么?其作用是什么?
答案:
- 核心配置文件:
mybatis-config.xml
- 作用:
- 配置 MyBatis 的运行环境,如数据库连接信息(数据源、事务管理器)、映射文件路径、类型别名等。
- 定义默认的数据库环境(通过
<environments>
标签的default
属性)。
实战案例:
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
2. MyBatis 中 #{}
和 ${}
的区别是什么?
答案:
#{}
:- 预编译处理,防止 SQL 注入。
- 适用于参数替换,如
SELECT * FROM users WHERE id = #{id}
。
${}
:- 字符串拼接,直接替换变量值,存在 SQL 注入风险。
- 适用于动态列名或表名,如
ORDER BY ${column}
。
实战案例:
-- 安全查询
SELECT * FROM users WHERE name = #{name};
-- 模糊查询(需注意安全)
SELECT * FROM users WHERE name LIKE '%${keyword}%';
二、MyBatis 查询与结果映射
3. MyBatis 如何实现多表关联查询?
答案:
-
方式:
- 嵌套查询:通过
<association>
和<collection>
标签实现一对一、一对多关联。 - 联合查询:通过
JOIN
语句一次性查询所有数据。
- 嵌套查询:通过
-
实现原理:
- 嵌套查询:先查询主表数据,再根据主表字段值查询关联表(延迟加载可减少数据库压力)。
- 联合查询:通过
<resultMap>
映射关联字段,避免多次查询。
实战案例:
<!-- 一对一关联 -->
<resultMap id="userResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="role" column="role_id" javaType="Role"
select="selectRoleById"/>
</resultMap>
<select id="selectUserById" resultMap="userResultMap">
SELECT * FROM users WHERE id = #{id};
</select>
<select id="selectRoleById" resultType="Role">
SELECT * FROM roles WHERE id = #{id};
</select>
4. MyBatis 的 resultType
和 resultMap
有什么区别?
答案:
resultType
:- 自动匹配数据库列名与实体类属性名(大小写不敏感)。
- 适用于简单映射场景。
resultMap
:- 自定义列与属性的映射关系,支持复杂映射(如嵌套对象、集合)。
- 推荐用于多表关联或字段名不一致的场景。
实战案例:
<!-- 使用 resultMap 映射复杂字段 -->
<resultMap id="orderResultMap" type="Order">
<id property="orderId" column="order_id"/>
<result property="productName" column="product_name"/>
<collection property="items" ofType="OrderItem"
column="order_id" select="selectItemsByOrderId"/>
</resultMap>
三、MyBatis 动态 SQL 与性能优化
5. MyBatis 的动态 SQL 有哪些标签?如何使用?
答案:
- 常用标签:
<if>
:条件判断(如WHERE name = #{name}
)。<choose>/<when>/<otherwise>
:分支选择。<foreach>
:循环遍历(如批量插入、IN 查询)。<set>
:更新操作时自动去除末尾逗号。<where>
:动态生成 WHERE 子句。
实战案例:
<select id="searchUsers" resultType="User">
SELECT * FROM users
<where>
<if test="name != null">
AND name LIKE '%${name}%'
</if>
<if test="age != null">
AND age > #{age}
</if>
</where>
</select>
6. MyBatis 如何实现分页查询?
答案:
- 方式:
- RowBounds 分页:通过限制起始行和行数实现(简单但不推荐,无法拦截 SQL)。
- PageHelper 插件:自动拦截 SQL,添加
LIMIT
或OFFSET
子句。
实战案例:
// 使用 PageHelper 插件
PageHelper.startPage(1, 10);
List<User> users = userMapper.selectAll();
PageInfo<User> pageInfo = new PageInfo<>(users);
四、MyBatis 缓存机制与性能调优
7. MyBatis 的一级缓存和二级缓存有什么区别?
答案:
- 一级缓存(SqlSession 级别):
- 默认开启,生命周期与 SqlSession 一致。
- 适用于单次会话内的重复查询。
- 二级缓存(Mapper 级别):
- 需手动配置,生命周期与 Mapper 关联。
- 适用于多会话共享的高频查询数据。
实战案例:
<!-- 启用二级缓存 -->
<cache eviction="FIFO" size="1024"/>
<!-- 配置二级缓存 -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
8. 如何防止 MyBatis 中的 SQL 注入?
答案:
- 核心方法:
- 使用
#{}
替代${}
,避免用户输入直接拼接到 SQL 中。 - 对用户输入进行校验和过滤(如白名单机制)。
- 结合数据库的预编译功能(如
PreparedStatement
)。
- 使用
实战案例:
-- 安全写法
SELECT * FROM users WHERE name = #{name};
-- 不安全写法(避免使用)
SELECT * FROM users WHERE name = '${name}';
五、MyBatis 高级功能与插件开发
9. 如何实现 MyBatis 的延迟加载?
答案:
- 原理:
- 使用 CGLIB 创建代理对象,当访问关联对象时触发额外查询。
- 通过
lazyLoadEnabled
配置启用。
实战案例:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
10. 如何编写 MyBatis 插件?
答案:
- 步骤:
- 实现
Interceptor
接口并重写intercept()
方法。 - 通过
@Intercepts
注解指定拦截的目标方法(如Executor.query()
)。 - 在配置文件中注册插件。
- 实现
实战案例:
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class})})
public class MyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 在 SQL 执行前后添加逻辑
return invocation.proceed();
}
}
六、MyBatis 与 Spring 集成
11. MyBatis 与 Spring 如何集成?
答案:
- 核心组件:
SqlSessionFactoryBean
:创建SqlSessionFactory
。MapperScannerConfigurer
:自动扫描 Mapper 接口。
- 配置方式:
- 通过 XML 配置或 Java 注解(如
@MapperScan
)。
- 通过 XML 配置或 Java 注解(如
实战案例:
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
return factory.getObject();
}
}
七、MyBatis 实际应用场景与案例
12. 在电商平台中,如何使用 MyBatis 实现商品分类的树形结构查询?
答案:
- 实现思路:
- 查询所有分类数据,并按父类 ID 建立父子关系。
- 使用递归算法(如深度优先搜索)构建树形结构。
实战案例:
public List<Category> buildCategoryTree(List<Category> categories) {
Map<Long, Category> categoryMap = new HashMap<>();
for (Category category : categories) {
categoryMap.put(category.getId(), category);
}
List<Category> rootCategories = new ArrayList<>();
for (Category category : categories) {
if (category.getParentId() == 0) {
rootCategories.add(category);
} else {
Category parent = categoryMap.get(category.getParentId());
if (parent != null) {
parent.getChildren().add(category);
}
}
}
return rootCategories;
}
八、总结
MyBatis 以其灵活的 SQL 控制能力和高效的性能优化策略,成为 Java 开发中不可或缺的持久层框架。通过掌握其核心概念(如动态 SQL、缓存机制)、高级功能(如插件开发、延迟加载)以及实际应用技巧(如多表关联查询、分页优化),开发者可以高效应对复杂的业务需求。在实际面试中,结合具体场景和代码示例进行解析,能够更直观地展示对 MyBatis 的深入理解与应用能力。