MyBatis核心机制与面试高频问题解析
占位符 #{} 与 ${} 的本质差异
在 MyBatis 中,#{} 和 ${} 是两种不同的参数处理方式:
#{}采用预编译机制,将参数作为占位符注入 SQL 语句,有效防止 SQL 注入攻击。${}则是直接字符串替换,会将传入的值原样拼接到 SQL 中,存在安全隐患。- 因此,推荐使用
#{}进行参数绑定,确保安全性和可维护性。
实体属性与数据库字段不一致时的解决方案
当实体类属性名与数据库字段名不匹配时,可通过以下两种方式解决:
方案一:通过别名映射
在 SQL 查询中为字段指定别名,使其与实体类属性名称一致。
<select id="selectOrder" parameterType="int" resultType="me.gacl.domain.Order">
SELECT order_id AS id, order_no AS orderno, order_price AS price
FROM orders
WHERE order_id = #{id}
</select>
方案二:使用 <resultMap> 显式映射
定义一个结果映射,明确指定数据库字段与实体属性之间的对应关系。
<resultMap type="me.gacl.domain.Order" id="orderResultMap">
<id property="id" column="order_id" />
<result property="orderno" column="order_no" />
<result property="price" column="order_price" />
</resultMap>
<select id="getOrder" parameterType="int" resultMap="orderResultMap">
SELECT * FROM orders WHERE order_id = #{id}
</select>
相比第一种,<resultMap> 更加灵活且易于维护,推荐优先使用。
获取自增主键值的方法
插入数据后获取生成的主键(如 MySQL 自增 ID),可通过 <selectKey> 标签实现:
MySQL 环境:插入后获取主键
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey keyProperty="id" order="AFTER" resultType="int">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO USER(username, birthday, sex, address)
VALUES(#{username}, #{birthday}, #{sex}, #{address})
</insert>
Oracle 环境:先获取序列值再插入
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey keyProperty="id" order="BEFORE" resultType="int">
SELECT 序列.nextval FROM DUAL
</selectKey>
INSERT INTO USER(id, username, birthday, sex, address)
VALUES(序列.nextval, #{username}, #{birthday}, #{sex}, #{address})
</insert>
多参数传递的实现方式
在映射器方法中传递多个参数,有以下几种常见做法:
1. 使用 @Param 注解命名参数
在接口方法中使用 @Param 注解为参数命名,便于 XML 中引用。
public interface UserMapper {
User selectUser(@Param("username") String username,
@Param("password") String password);
}
<select id="selectUser" resultType="User">
SELECT id, username, password
FROM users
WHERE username = #{username}
AND password = #{password}
</select>
2. 通过 Map 集合封装参数
将多个参数放入 Map,以 key-value 形式传递。
// Java 调用代码
Map<String, Object> params = new HashMap<>();
params.put("start", 0);
params.put("end", 10);
return sqlSession.selectList("StudentMapper.pagination", params);
<!-- XML 中使用 -->
<select id="pagination" parameterType="map" resultMap="studentMap">
SELECT * FROM students LIMIT #{start}, #{end}
</select>
动态 SQL 的作用与执行原理
动态 SQL 允许根据条件动态拼接 SQL 语句,提升灵活性。常用标签包括:if、where、set、foreach、choose、when、otherwise 等。
其底层基于 OGNL 表达式引擎,运行时根据参数对象计算表达式值,并动态生成最终的 SQL 语句。
不同 XML 文件中 ID 是否可重复?
若映射文件设置了 namespace 属性,则各文件内的 id 可重复,因为完整标识为 namespace + id。未设置 namespace 时,相同 id 会导致覆盖。
为何称 MyBatis 为半自动 ORM 框架?
MyBatis 不同于 Hibernate 这类全自动 ORM 工具。它需要开发者手动编写关联查询的 SQL 语句,仅负责将结果映射为对象,因此称为"半自动"。而 Hibernate 可通过对象关系模型自动推导关联查询,属于全自动。
Mapper 接口的工作机制与重载限制
Mapper 接口无实现类,由 MyBatis 在运行时通过 JDK 动态代理生成代理对象。调用方法时,系统依据接口全限定名 + 方法名定位对应的 MappedStatement 对象并执行相应 SQL。
由于依赖唯一标识,**接口方法不可重载**(即不能有相同方法名但参数不同的多个方法)。
MyBatis 与 IBatis 的主要改进点
- 支持接口绑定(注解或 XML 方式)
- 动态 SQL 改用 OGNL 表达式语法
- 引入
association(一对一)和collection(一对多)标签用于复杂对象映射
接口绑定的实现方式
有两种绑定方式:
- 注解绑定:在接口方法上添加
@Select、@Insert等注解,内嵌 SQL 语句。 - XML 绑定:在 XML 映射文件中编写 SQL,需确保
namespace与接口全路径一致。
分页机制与插件原理
MyBatis 原生支持内存分页(通过 RowBounds),但更常用的是物理分页插件(如 PageHelper)。
分页插件原理:利用 MyBatis 插件机制拦截 Executor 的执行过程,在执行前修改原始 SQL,添加数据库方言相关的分页语句(如 LIMIT)。
例如:将 SELECT * FROM student 重写为 SELECT t.* FROM (SELECT * FROM student) t LIMIT 0,10。
插件开发原理与实现步骤
MyBatis 插件仅支持对以下四个核心接口的拦截:
ParameterHandlerResultSetHandlerStatementHandlerExecutor
实现方式:实现 Interceptor 接口,重写 intercept() 方法,并通过注解指定要拦截的接口及方法。最后在配置文件中注册该插件。
延迟加载机制及其原理
MyBatis 支持 association(一对一)和 collection(一对多)的延迟加载。开启方式:
<setting name="lazyLoadingEnabled" value="true"/>
实现原理:使用 CGLIB 为关联对象创建代理。首次访问关联属性时,若值为 null,会触发独立查询,获取数据后再赋值,完成后续操作。
三种 Executor 执行器的区别
- SimpleExecutor:每次执行都新建
Statement,执行完毕立即关闭。 - ReuseExecutor:复用已存在的
Statement,缓存于 Map,避免频繁创建。 - BatchExecutor:批量执行更新操作,将多条
INSERT/UPDATE添加到批处理队列,统一提交,适用于大批量数据插入。
三种执行器的作用范围均限于 SqlSession 生命周期内。
MyBatis 与 Hibernate 的核心差异
MyBatis 并非完全的 ORM 框架,强调开发者自主控制 SQL 语句。优势在于:
- 学习成本低,易于上手
- SQL 可精细优化,性能可控
- 适合需求变化频繁的项目(如互联网应用)
而 Hibernate 强调对象关系映射,具备良好的数据库无关性,适合固定需求的定制化系统,但学习曲线陡峭,调优难度高。
选择框架应以实际业务场景为准,合适才是最佳。