Mybatis面试题目

1. 什么是 MyBatis?

一句话总结 :MyBatis 是一款轻量级的半自动 ORM(对象关系映射)框架,专注于 SQL 映射,将 Java 对象与数据库表进行关联,简化 JDBC 操作,允许开发者自定义 SQL、存储过程和高级映射。核心要点

  • 前身是 iBatis,2010 年更名 MyBatis;
  • 摒弃 JDBC 繁琐的手动参数设置和结果集解析,通过 XML / 注解配置 SQL 与 Java 对象的映射;
  • 核心思想:"半自动化"------ 开发者写 SQL,框架处理参数绑定、结果映射、连接管理等。

2. MyBatis 的优缺点?

一句话总结 :MyBatis 的优势是灵活可控 SQL、轻量易集成,缺点是需手动写 SQL、配置工作量大、移植性差。核心要点

  • 优点:
    1. 高度灵活,可自定义 SQL,适配复杂业务场景(如多表联查、存储过程);
    2. 轻量级,无第三方依赖,学习成本低;
    3. 支持动态 SQL、缓存、延迟加载等特性;
    4. 与 Spring 生态无缝集成。
  • 缺点:
    1. 需手动编写 SQL,开发效率低于全自动 ORM(如 Hibernate);
    2. SQL 与代码耦合(XML / 注解),维护成本高;
    3. 数据库移植性差(SQL 语法依赖具体数据库);
    4. 结果集映射需手动配置,易出错。

3. #{} 和 ${} 的区别是什么?

一句话总结 :#{} 是预编译占位符(安全,防 SQL 注入),${} 是字符串拼接(不安全,适用于动态表名 / 列名)。核心要点

特性 #{} ${}
底层实现 PreparedStatement Statement
处理方式 预编译,参数占位符 字符串直接拼接
SQL 注入 防止 无法防止
适用场景 普通参数(值) 动态表名 / 列名 / 排序
示例 WHERE id = #{id} ORDER BY ${col}

4. xml 映射文件中有哪些标签?

一句话总结 :MyBatis XML 映射文件包含核心 SQL 标签、动态 SQL 标签、配置标签,覆盖 SQL 执行、参数处理、结果映射全流程。核心要点

  • 核心 SQL 标签:<select>(查询)、<insert>(插入)、<update>(更新)、<delete>(删除);
  • 动态 SQL 标签:<if><where><choose>/<when>/<otherwise><trim><set><foreach>
  • 配置 / 辅助标签:<resultMap>(结果映射)、<parameterMap>(参数映射,已过时)、<sql>(可复用 SQL 片段)、<include>(引用 SQL 片段)、<cache>(缓存配置)。

5. 模糊查询 like 语句该怎么写?

一句话总结 :模糊查询推荐用#{}拼接通配符(安全),避免直接用${}(易注入),有两种常用写法。核心要点

  • 写法 1(推荐,Java 层拼接通配符):

    java 复制代码
    // 代码层
    String keyword = "%" + "张三" + "%";
    userMapper.selectLike(keyword);
    XML 复制代码
    <!-- XML映射文件 -->
    <select id="selectLike" resultType="User">
      SELECT * FROM user WHERE name LIKE #{keyword}
    </select>
  • 写法 2(SQL 层拼接,需注意数据库语法):

    XML 复制代码
    <select id="selectLike" resultType="User">
      <!-- MySQL -->
      SELECT * FROM user WHERE name LIKE CONCAT('%', #{keyword}, '%')
      <!-- Oracle -->
      <!-- SELECT * FROM user WHERE name LIKE '%' || #{keyword} || '%' -->
    </select>
  • 注意:禁止写LIKE '%${keyword}%',会导致 SQL 注入。

6. Mapper 接口的工作原理是什么?Mapper 接口里的方法,参数不同时,方法能重载吗?

一句话总结 :Mapper 接口基于 JDK 动态代理生成实现类,通过接口全类名 + 方法名匹配 XML 中的 SQL;Mapper 方法不能重载 (id 唯一)。核心要点

  • 工作原理:
    1. MyBatis 启动时,扫描 Mapper 接口并注册到MapperRegistry
    2. 调用getMapper()时,JDK 动态代理生成 Mapper 接口的代理类;
    3. 代理类根据 "接口全类名 + 方法名" 匹配 XML / 注解中的 SQL 节点(id 需与方法名一致);
    4. 执行 SQL 并处理参数 / 结果映射。
  • 方法重载:Mapper 接口方法不能重载 ------XML 中 SQL 的id是唯一的,重载方法会导致 id 冲突,MyBatis 无法区分。

7. MyBatis 是如何进行分页的?分页插件的原理是什么?

一句话总结 :MyBatis 原生需手动写分页 SQL,分页插件(如 PageHelper)基于拦截器改写 SQL,自动拼接分页语句。核心要点

  • 原生分页(手动实现):

    XML 复制代码
    <!-- MySQL -->
    <select id="selectPage" resultType="User">
      SELECT * FROM user LIMIT #{offset}, #{size}
    </select>
    <!-- Oracle -->
    <!-- SELECT * FROM (SELECT t.*, ROWNUM rn FROM user t WHERE ROWNUM <= #{end}) WHERE rn >= #{start} -->
  • 分页插件(PageHelper)原理:

    1. 基于 MyBatis 的Interceptor拦截器,拦截Executorquery方法;
    2. 执行 SQL 前,根据方言(MySQL/Oracle)自动拼接LIMIT/ROWNUM分页语句;
    3. 执行分页 SQL,同时查询总条数(COUNT(*));
    4. 将分页结果封装为Page对象返回。

8. Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?

一句话总结 :MyBatis 通过反射创建目标对象,根据配置映射列名与属性名,支持自动映射和手动映射两种形式。核心要点

  • 封装流程:
    1. 执行 SQL 获取ResultSet
    2. 根据resultType/resultMap创建目标对象(反射);
    3. 映射列值到对象属性(匹配列名与属性名);
    4. 返回单个对象 / 集合。
  • 映射形式:
    1. 自动映射(resultType):列名与属性名一致时,无需手动配置,直接映射;
    2. 手动映射(resultMap):列名与属性名不一致、关联查询(一对一 / 一对多)时,自定义映射规则(<id>/<result>/<association>/<collection>)。

9. 如何执行批量插入?能返回主键 id 吗

一句话总结 :MyBatis 通过<foreach>标签实现批量插入,支持返回自增主键(MySQL)或序列主键(Oracle)。核心要点

  • 批量插入(MySQL):

    XML 复制代码
    <insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">
      INSERT INTO user (name, age) VALUES
      <foreach collection="list" item="item" separator=",">
        (#{item.name}, #{item.age})
      </foreach>
    </insert>
  • 返回主键:

    1. MySQL(自增主键):<insert>添加useGeneratedKeys="true" + keyProperty="id",插入后实体类的id会被赋值;

    2. Oracle(序列主键):

      XML 复制代码
      <insert id="batchInsert">
        <selectKey keyProperty="id" order="BEFORE" resultType="Long">
          SELECT SEQ_USER.NEXTVAL FROM DUAL
        </selectKey>
        INSERT INTO user (id, name, age) VALUES
        <foreach collection="list" item="item" separator=",">
          (#{item.id}, #{item.name}, #{item.age})
        </foreach>
      </insert>

10. MyBatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?

一句话总结 :动态 SQL 用于根据不同条件拼接 SQL 语句,核心标签有<if>/<where>等,底层通过 OGNL 表达式解析条件、拼接 SQL。核心要点

  • 作用:解决静态 SQL 硬编码问题,适配多条件查询、动态更新等场景;
  • 核心动态 SQL 标签:<if>(条件判断)、<where>(自动拼接 WHERE 并去除多余 AND/OR)、<set>(更新时自动拼接 SET 并去除多余逗号)、<foreach>(循环遍历,如批量插入)、<choose>/<when>/<otherwise>(多条件分支);
  • 执行原理:
    1. MyBatis 解析 XML 时,将动态 SQL 标签解析为SqlNode对象;
    2. 执行 SQL 前,通过 OGNL 表达式解析条件参数;
    3. 根据条件拼接SqlNode对应的 SQL 片段,生成最终可执行的 SQL。

11. MyBatis 能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别。

一句话总结 :MyBatis 支持一对一 / 一对多关联查询,实现方式有嵌套查询(延迟加载)和嵌套结果(一次性加载),前者性能优、后者减少查询次数。核心要点

表格

关联类型 实现方式 标签 特点
一对一 嵌套结果 <association> 单 SQL 联查,一次性加载所有数据,结果映射到关联对象,适用于数据量小的场景
一对一 嵌套查询 <association> 多 SQL 查询,先查主表,再根据主键查关联表,支持延迟加载,减少无效查询
一对多 嵌套结果 <collection> 单 SQL 联查,一次性加载主表 + 子表数据,结果映射为集合,易产生数据冗余
一对多 嵌套查询 <collection> 多 SQL 查询,先查主表,再批量查子表,支持延迟加载,性能更优
  • 示例(一对多嵌套查询):

    XML 复制代码
    <resultMap id="UserMap" type="User">
      <id column="id" property="id"/>
      <result column="name" property="name"/>
      <collection property="orders" select="selectOrdersByUserId" column="id" fetchType="lazy"/>
    </resultMap>

12. MyBatis 是否支持延迟加载?如果支持,它的实现原理是什么?

一句话总结 :MyBatis 支持延迟加载(懒加载),仅在访问关联对象时才执行查询,底层基于动态代理拦截属性访问。核心要点

  • 开启方式:配置文件中设置lazyLoadingEnabled=true(全局),或关联查询标签中fetchType="lazy"(局部);
  • 实现原理:
    1. 延迟加载时,MyBatis 为关联对象生成动态代理(CGLIB/JDK);
    2. 首次访问关联对象的属性时,触发代理类的invoke方法;
    3. 代理类执行预设的关联查询 SQL,加载数据并赋值给关联对象;
    4. 后续访问直接使用已加载的数据。
  • 适用场景:一对多 / 多对多关联查询,减少初始查询的数据量。

13. MyBatis 的 xml 映射文件中,不同的 xml 映射文件的 id 是否可以重复?

一句话总结 :不同 XML 映射文件的id可以重复 ,但需保证 "命名空间(namespace)+ id" 唯一(namespace 对应 Mapper 接口全类名)。核心要点

  • MyBatis 通过namespace + id唯一标识一个 SQL 节点,而非仅id
  • 示例:UserMapper.xmlid="selectById"OrderMapper.xmlid="selectById"可共存,因为 namespace 不同;
  • 注意:同一 XML 映射文件内的id必须唯一。

14. MyBatis 都有哪些 Executor 执行器?它们之间的区别是什么?

一句话总结 :MyBatis 有 3 种 Executor 执行器(Simple/Reuse/Batch),核心区别在于 Statement 的复用和批量处理能力。核心要点

表格

执行器类型 核心特点 适用场景
Simple 每次执行 SQL 都创建新的 Statement,执行后关闭,无复用 普通查询 / 少量操作
Reuse 复用 Statement(按 SQL 语句缓存),同一 SQL 重复执行时无需重新创建 相同 SQL 多次执行
Batch 批量执行 SQL,缓存 Statement 并批量提交,减少数据库交互 大批量插入 / 更新 / 删除

15. MyBatis 中如何指定使用哪一种 Executor 执行器?

一句话总结 :通过配置文件 / 代码指定 Executor 类型,默认 Simple,可全局配置或局部指定。核心要点

  • 全局配置(mybatis-config.xml):

    XML 复制代码
    <settings>
      <!-- SIMPLE/REUSE/BATCH -->
      <setting name="defaultExecutorType" value="BATCH"/>
    </settings>
  • 局部指定(代码层,针对单个 SqlSession):

    java 复制代码
    // 获取批量执行器的SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    // 执行批量操作
    mapper.batchInsert(list);
    sqlSession.commit();

16. MyBatis 是否可以映射 Enum 枚举类?

一句话总结 :MyBatis 支持映射 Enum 枚举类,可映射枚举名称(默认)或自定义值(需类型处理器)。核心要点

  • 方式 1(默认,映射枚举名称):

    java 复制代码
    // 枚举类
    public enum Sex { MALE, FEMALE }
    // 实体类
    public class User { private Sex sex; }
    // XML映射(直接用枚举名称)
    <insert id="insertUser">
      INSERT INTO user (sex) VALUES (#{sex})
    </insert>
  • 方式 2(自定义值,需类型处理器):

    java 复制代码
    // 枚举类(含自定义值)
    public enum Sex {
      MALE(1), FEMALE(2);
      private int code;
      // 构造器+getter
    }
    // 自定义类型处理器
    public class SexTypeHandler extends BaseTypeHandler<Sex> {
      @Override
      public void setNonNullParameter(PreparedStatement ps, int i, Sex sex, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, sex.getCode());
      }
      // 实现其他方法(getNullableResult)
    }
    XML 复制代码
    <!-- 配置类型处理器 -->
    <resultMap id="UserMap" type="User">
      <result column="sex" property="sex" typeHandler="com.example.SexTypeHandler"/>
    </resultMap>

17. 简述 MyBatis 的 xml 映射文件和 MyBatis 内部数据结构之间的映射关系?

一句话总结 :MyBatis 解析 XML 映射文件后,将标签解析为对应的 Java 对象(MappedStatement/SqlSource 等),存储在 Configuration 中。核心要点

  • 解析流程:
    1. MyBatis 启动时,解析 XML 映射文件的每个 SQL 标签(<select>/<insert>等);
    2. 每个 SQL 标签解析为MappedStatement对象(核心,包含 SQL、参数、结果映射等);
    3. MappedStatement中的 SQL(含动态标签)解析为SqlSource对象,动态 SQL 进一步解析为SqlNode
    4. 结果映射(<resultMap>)解析为ResultMap对象;
    5. 所有解析后的对象存储在Configuration(全局配置)中,供执行 SQL 时调用。

18. 为什么说 MyBatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?

一句话总结 :MyBatis 需手动编写 SQL(半自动),全自动 ORM(如 Hibernate)无需写 SQL,仅通过注解 / 配置映射对象与表。核心要点

特性 MyBatis(半自动 ORM) Hibernate(全自动 ORM)
SQL 编写 手动编写,灵活可控 自动生成,无需手动写
映射方式 需配置 SQL 与对象映射 仅需配置对象与表映射
数据库移植性 差(SQL 依赖数据库) 好(自动适配方言)
学习成本 低(专注 SQL) 高(需掌握 HQL / 缓存等)
适用场景 复杂 SQL、性能优化 简单 CRUD、快速开发

19. Mybatis 的一级、二级缓存?

一句话总结 :一级缓存是 SqlSession 级别的本地缓存(默认开启),二级缓存是 Mapper 级别的全局缓存(需手动开启),均用于减少数据库查询。核心要点

缓存级别 作用域 开启方式 失效场景
一级缓存 SqlSession 默认开启,无需配置 1. SqlSession 关闭 / 提交 / 回滚;2. 执行更新操作;3. 手动清除
二级缓存 Mapper(namespace) 1. 全局配置cacheEnabled=true;2. XML 中加<cache> 1. 同一 namespace 执行更新操作;2. 缓存过期;3. 手动清除
  • 执行顺序:查询时先查二级缓存→一级缓存→数据库;更新时先清空对应缓存→执行 SQL。

20. 简述 Mybatis 的插件运行原理,以及如何编写一个插件

一句话总结 :MyBatis 插件基于 JDK 动态代理和拦截器接口,拦截四大核心组件(Executor/StatementHandler 等)的方法,编写插件需实现 Interceptor 接口并配置拦截规则。核心要点

  • 运行原理:

    1. MyBatis 插件本质是Interceptor,可拦截四大组件:Executor(执行器)、StatementHandler(SQL 执行)、ParameterHandler(参数处理)、ResultSetHandler(结果处理);
    2. 启动时,插件注册到InterceptorChain
    3. 创建核心组件时,通过 JDK 动态代理生成代理类,将插件逻辑织入目标方法;
    4. 执行目标方法时,触发代理类的invoke方法,执行插件的拦截逻辑。
  • 编写插件步骤:

    1. 实现Interceptor接口,重写intercept(核心拦截逻辑)、plugin(生成代理对象)、setProperties(配置参数);
    2. @Intercepts注解指定拦截的组件和方法;
    3. 注册插件到 MyBatis 配置文件。
  • 示例(拦截 Executor 的 query 方法):

    java 复制代码
    @Intercepts({
      @Signature(type = Executor.class, method = "query", 
                 args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    })
    public class MyPlugin implements Interceptor {
      @Override
      public Object intercept(Invocation invocation) throws Throwable {
        // 前置逻辑:如打印SQL执行时间
        long start = System.currentTimeMillis();
        Object result = invocation.proceed(); // 执行原方法
        // 后置逻辑
        long end = System.currentTimeMillis();
        System.out.println("SQL执行时间:" + (end - start) + "ms");
        return result;
      }
    
      @Override
      public Object plugin(Object target) {
        return Plugin.wrap(target, this); // 生成代理对象
      }
    
      @Override
      public void setProperties(Properties properties) {
        // 读取插件配置参数
      }
    }
    XML 复制代码
    <!-- 注册插件 -->
    <plugins>
      <plugin interceptor="com.example.MyPlugin">
        <property name="param1" value="value1"/>
      </plugin>
    </plugins>

整体核心要点回顾

  1. MyBatis 核心是 "半自动 ORM",灵活可控 SQL,通过 XML / 注解实现 SQL 与 Java 对象映射,支持动态 SQL、缓存、延迟加载等特性;
  2. 关键特性:#{}防注入、<foreach>批量操作、resultMap复杂映射、Executor 执行器适配不同 SQL 执行场景;
  3. 扩展能力:分页插件基于拦截器改写 SQL,自定义插件拦截核心组件实现功能增强,二级缓存提升查询性能。
相关推荐
xlp666hub2 小时前
嵌入式 Linux 启动:设备树的加载、传递和解析全流程分析
linux·面试
程序员库里2 小时前
TipTap简介
前端·javascript·面试
小涛不学习3 小时前
WebSocket 技术详解(原理 + 使用 + 面试总结)
websocket·网络协议·面试
java1234_小锋3 小时前
Java高频面试题:MyBatis与JPA有哪些不同?
java·开发语言·mybatis·jpa
gameboy0313 小时前
【异常解决】Unable to start embedded Tomcat Nacos 启动报错
java·tomcat
gameboy0313 小时前
Windows操作系统部署Tomcat详细讲解
java·windows·tomcat
马士兵教育3 小时前
程序员空窗期如何解决?
人工智能·面试·职场和发展
草履虫建模3 小时前
面试常问 SQL 优化八股文总结:慢查询、索引失效、回表、覆盖索引一次搞懂
java·数据库·spring boot·sql·面试·职场和发展·数据库架构
jinanmichael4 小时前
Mybatis控制台打印SQL执行信息(执行方法、执行SQL、执行时间)
数据库·sql·mybatis