1. #{} 和 ${} 的区别是什么?
答案:
| 对比项 | #{} |
${} |
|---|---|---|
| 处理方式 | 预编译占位符(?),使用 PreparedStatement |
字符串直接替换,使用 Statement |
| 安全性 | 防止 SQL 注入 | 存在 SQL 注入风险 |
| 使用场景 | 大部分参数传递 | 表名、列名、ORDER BY 等动态字段 |
| 示例 | WHERE id = #{id} → WHERE id = ? |
ORDER BY ${column} → ORDER BY name |
面试加分点 :优先使用 #{},只有当传入的是表名、列名、ORDER BY 等无法使用占位符的场景才用 ${}。
2. MyBatis 的动态 SQL 有哪些常用标签?
答案:
| 标签 | 作用 | 示例 |
|---|---|---|
<if> |
条件判断 | WHERE id = #{id} AND name = #{name} |
<choose> |
多分支选择(类似 switch) | <choose><when test=...><otherwise> |
<where> |
自动处理 WHERE 关键字和多余的 AND/OR | 包裹条件,智能加 WHERE 和去多余连接词 |
<set> |
自动处理 SET 关键字和尾部逗号 | 用于 update 语句,智能加 SET 和去多余逗号 |
<foreach> |
遍历集合 | IN (#{item}) |
<trim> |
自定义前缀、后缀及去除逻辑 | 更灵活的 <where> / <set> 底层实现 |
举例:
xml
<update id="updateUser" parameterType="User">
UPDATE user
<set>
<if test="name != null and name != ''">
name = #{name},
</if>
<if test="age != null">
age = #{age},
</if>
<if test="email != null and email != ''">
email = #{email},
</if>
</set>
WHERE id = #{id}
</update>
<!-- 使用 <trim> 模拟 <set> -->
<update id="updateUser">
UPDATE user
<trim prefix="SET" suffixOverrides=",">
<if test="name != null">name = #{name},</if>
<if test="age != null">age = #{age},</if>
<if test="email != null">email = #{email},</if>
</trim>
WHERE id = #{id}
</update>
3. MyBatis 如何解决实体类属性名和数据库字段名不一致的问题?
答案:
三种方式:
-
使用
resultMap(推荐)明确指定字段与属性的映射关系:
xml
<resultMap id="UserMap" type="User"> <result column="user_name" property="userName"/> <result column="phone_num" property="phoneNum"/> </resultMap> -
使用
@Results注解java
@Results({ @Result(column = "user_name", property = "userName"), @Result(column = "phone_num", property = "phoneNum") })
3、字段起别名
sql
select user_name as userName
4. resultType 和 resultMap 的区别?
答案:
| 对比项 | resultType |
resultMap |
|---|---|---|
| 用途 | 简单映射,字段名与属性名一致时使用 | 复杂映射,支持不一致、嵌套对象、关联查询 |
| 灵活性 | 低 | 高 |
| 是否支持一对多/多对一 | 不支持 | 支持 |
| 推荐场景 | 简单查询 | 复杂查询、关联查询 |
面试加分点:
resultType是 MyBatis 的自动映射 ,resultMap是手动映射,并能支持延迟加载。
5、MyBatis 的一级缓存和二级缓存有什么区别?
| 特性 | 一级缓存 | 二级缓存 |
|---|---|---|
| 作用范围 | SqlSession 内 | Mapper 的 namespace 内 |
| 默认 | 开启 | 关闭 |
| 生命周期 | 会话级别 | 应用级别(随 SqlSessionFactory) |
| 跨会话 | 不共享 | 共享 |
| 数据形式 | 对象引用 | 对象的副本(序列化) |
| 清空时机 | 执行 DML、clearCache()、会话关闭 | 执行 DML(同 namespace)、会话关闭时写入 |
| 最佳实践 | 单个请求内的重复查询 | 读多写少的静态数据 |
一级缓存(SqlSession 级别)
java
// 一、作用域演示
// 同一个 SqlSession
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user1 = mapper.selectById(1); // 执行 SQL
User user2 = mapper.selectById(1); // 命中一级缓存,不执行 SQL
session.close();
// 缓存的数据形式演示: 一级缓存:修改对象会影响缓存
User user1 = mapper.selectById(1);
user1.setName("新名字"); // 缓存里也被改了
二级缓存(NameSpace 级别)
java
// 一、作用域演示
// 会话1
SqlSession session1 = sqlSessionFactory.openSession();
UserMapper mapper1 = session1.getMapper(UserMapper.class);
User user1 = mapper1.selectById(1); // 执行 SQL,数据放入二级缓存
session1.close(); // 会话关闭,数据进入二级缓存
// 会话2(不同的 SqlSession)
SqlSession session2 = sqlSessionFactory.openSession();
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user2 = mapper2.selectById(1); // 命中二级缓存,不执行 SQL
session2.close();
// 二、缓存的数据形式演示:二级缓存:修改对象不影响缓存(因为返回的是副本)
User user1 = mapper.selectById(1);
user1.setName("新名字"); // 缓存里的对象不受影响
User user2 = mapper.selectById(1); // 返回的是新副本,name 还是旧值
6、MyBatis 如何实现分页?
| 方式 | 实现原理 | 性能 | 侵入性 | 配置复杂度 | 推荐场景 |
|---|---|---|---|---|---|
| 手动 Limit | 手写 LIMIT | 高 | 高 | 低 | 简单项目,查询少 |
| RowBounds | 全查+内存截取 | 极低 | 低 | 无 | 绝不使用 |
| PageHelper | SQL 拦截器 | 高 | 低 | 低 | 通用推荐 |
| MyBatis-Plus | 插件拦截 | 高 | 中(需要整个框架) | 中 | 已用 MyBatis-Plus 的项目 |
java
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public PageInfo<User> getUsersByPage(int pageNum, int pageSize) {
// 1. 设置分页参数(会拦截接下来的第一次查询)
PageHelper.startPage(pageNum, pageSize);
// 2. 执行查询(自动拼接 LIMIT)
List<User> users = userMapper.selectAll();
// 3. 包装成 PageInfo(包含总数、总页数等信息)
PageInfo<User> pageInfo = new PageInfo<>(users);
return pageInfo;
}
}
7、MyBatis 和 JPA/Hibernate 有什么区别?
答案:
| 对比项 | MyBatis | JPA/Hibernate |
|---|---|---|
| 类型 | 半自动 ORM(仅映射结果集) | 全自动 ORM(对象与表完全映射) |
| SQL 控制 | 完全手写 SQL,精细化控制 | 自动生成 SQL,可配置但难调优 |
| 学习成本 | 低 | 中高 |
| 开发效率 | 中等(需写 SQL) | 高(无需写 SQL) |
| 性能调优 | 容易,SQL 可针对性优化 | 困难,需理解 HQL 生成规则 |
| 数据库移植 | 差,SQL 与方言绑定 | 好,自动适配不同数据库 |
| 适用场景 | 复杂查询、高 SQL 调优需求 | CRUD 为主、需求快速开发 |
选择建议:
- 复杂查询、对 SQL 执行计划有要求 → 选 MyBatis
- 简单 CRUD、快速开发、数据库需要迁移 → 选 JPA/Hibernate