当前位置:首页 > 技术 > 正文内容

MyBatis核心机制与面试高频问题解析

访客 技术 2026年8月28日 1

占位符 #{} 与 ${} 的本质差异

在 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 语句,提升灵活性。常用标签包括:ifwheresetforeachchoosewhenotherwise 等。

其底层基于 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(一对多)标签用于复杂对象映射

接口绑定的实现方式

有两种绑定方式:

  1. 注解绑定:在接口方法上添加 @Select@Insert 等注解,内嵌 SQL 语句。
  2. 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 插件仅支持对以下四个核心接口的拦截:

  • ParameterHandler
  • ResultSetHandler
  • StatementHandler
  • Executor

实现方式:实现 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 强调对象关系映射,具备良好的数据库无关性,适合固定需求的定制化系统,但学习曲线陡峭,调优难度高。

选择框架应以实际业务场景为准,合适才是最佳。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。