java最新myBatis面试题及解答

106 阅读9分钟

以下是 2025年MyBatis高频面试题及详细解答,覆盖核心原理、实战应用、性能优化、高级特性等面试重点,适合中高级Java工程师求职备考:

一、基础概念类

1. 什么是MyBatis?它的核心优势是什么?

解答
MyBatis是一款基于Java的持久层框架,前身是iBatis,专注于SQL映射(将Java对象与SQL语句关联),简化JDBC开发流程(无需手动处理Connection、Statement、ResultSet等对象的创建/关闭)。
核心优势:

  • 半自动化ORM:SQL语句需手动编写,灵活可控(适合复杂SQL场景);
  • 低侵入性:无需实体类继承特定父类或实现接口;
  • 易于整合:无缝集成Spring、Spring Boot等主流框架;
  • 缓存机制:内置一级缓存(SqlSession级别)和二级缓存(Mapper级别),提升查询效率;
  • 动态SQL:通过标签(如​​<if>​​ ​​<where>​​)灵活拼接SQL,适配多条件查询。
2. MyBatis与Hibernate的区别?适用场景是什么?

解答

对比维度MyBatisHibernate
ORM类型半自动化(SQL手动编写)全自动化(SQL自动生成)
SQL控制度高(灵活优化复杂SQL)低(难优化复杂SQL)
学习成本低(专注SQL映射)高(需掌握HQL、缓存等)
适用场景复杂SQL、性能要求高、互联网项目简单CRUD、快速开发、企业级应用

二、核心原理类

3. MyBatis的核心组件有哪些?各自作用是什么?

解答
MyBatis核心组件通过“职责链模式”协同工作,核心包括:

  • SqlSessionFactory:工厂类,由​​SqlSessionFactoryBuilder​​读取配置文件(mybatis-config.xml)创建,用于生成SqlSession;
  • SqlSession:会话对象,代表与数据库的一次连接,提供CRUD操作的API(如​​selectOne()​​ ​​insert()​​),线程不安全,需用完关闭;
  • Mapper接口:自定义DAO接口,MyBatis通过动态代理生成实现类,无需手动编写Impl;
  • Mapper.xml:SQL映射文件,存储SQL语句、参数映射、结果集映射(​​<select>​​ ​​<insert>​​ ​​<resultMap>​​等标签);
  • Executor:执行器(MyBatis核心执行引擎),负责SQL解析、参数处理、结果映射,分为​​SimpleExecutor​​(默认,简单执行)、​​ReuseExecutor​​(复用Statement)、​​BatchExecutor​​(批量执行);
  • StatementHandler:处理JDBC Statement对象,负责参数设置、SQL执行、结果集封装;
  • ResultSetHandler:将JDBC ResultSet映射为Java对象。
4. MyBatis的工作原理(执行流程)是什么?

解答

  1. 加载配置:​​SqlSessionFactoryBuilder​​读取​​mybatis-config.xml​​(全局配置)和​​Mapper.xml​​(SQL映射),解析后生成​​SqlSessionFactory​​;
  2. 创建会话:通过​​SqlSessionFactory.openSession()​​创建​​SqlSession​​(默认不自动提交事务);
  3. 获取代理:​​SqlSession.getMapper(Mapper接口.class)​​通过JDK动态代理生成Mapper接口的代理对象;
  4. 执行SQL:调用Mapper接口方法,代理对象触发​​Executor​​执行,​​Executor​​通过​​StatementHandler​​处理JDBC操作:
  • 解析SQL(处理动态SQL、参数占位符);
  • 绑定参数(将Java对象参数设置到PreparedStatement);
  • 执行SQL,获取ResultSet;
  • ​ResultSetHandler​​将ResultSet映射为Java对象;
  1. 关闭会话:执行完成后,关闭​​SqlSession​​(释放数据库连接)。

三、实战应用类

5. MyBatis中#{}和${}的区别?为什么推荐用#{}?

解答

对比维度#{}${}
本质预编译参数占位符(?)字符串拼接
安全性防SQL注入(参数自动转义)存在SQL注入风险(直接拼接)
适用场景传递参数(如查询条件、新增字段值)动态表名、动态列名(如​​ORDER BY ${column}​​)

示例

  • ​select * from user where id = #{id}​​ → 编译为​​select * from user where id = ?​​(参数通过​​setInt()​​设置,安全);
  • ​select * from user where id = ${id}​​ → 若id传入​​1 or 1=1​​,则拼接为​​select * from user where id = 1 or 1=1​​(注入风险)。
    结论:优先用​​#{}​​,仅动态表名/列名时用​​${}​​(需手动过滤参数,避免注入)。
6. MyBatis如何实现多参数传递?

解答:MyBatis支持4种多参数传递方式,核心是“让MyBatis识别参数名”:

  1. @Param注解(推荐) :在Mapper接口方法参数上添加​​@Param("参数名")​​,Mapper.xml中直接通过​​#{参数名}​​引用:
User selectByUsernameAndAge(@Param("username") String username, @Param("age") int age);
<select id="selectByUsernameAndAge" resultType="User">
  select * from user where username = #{username} and age = #{age}
</select>
  1. Map传递:参数封装为Map,Mapper.xml通过​​#{map的key}​​引用(不推荐,可读性差);
  2. 实体类传递:参数为自定义实体类,Mapper.xml通过​​#{实体类属性名}​​引用(适合参数较多场景);
  3. 默认参数名(MyBatis 3.4+) :无需注解,直接用​​#{arg0} #{arg1}​​​或​​#{param1} #{param2}​​引用(arg从0开始,param从1开始)。
7. 什么是ResultMap?为什么要用它?

解答
​​​ResultMap​​是MyBatis中结果集映射的核心标签,用于定义Java对象与数据库表字段的映射关系(解决“字段名与属性名不一致”问题)。
使用场景

  • 数据库字段名与实体类属性名不同(如数据库​​user_name​​​ vs 实体​​userName​​);
  • 复杂查询(多表关联、嵌套查询、集合属性映射);
  • 避免SQL中写​​AS​​​别名(简化SQL)。
    示例
<resultMap id="UserResultMap" type="User">
  <id column="user_id" property="userId"/> <!-- 主键映射 -->
  <result column="user_name" property="userName"/> <!-- 普通字段映射 -->
  <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/> <!-- 类型指定 -->
</resultMap>

<select id="selectById" resultMap="UserResultMap">
  select user_id, user_name, create_time from user where user_id = #{id}
</select>

四、动态SQL类

8. MyBatis动态SQL的常用标签有哪些?各自作用是什么?

解答:动态SQL是MyBatis的核心特性,通过标签拼接灵活的SQL语句,常用标签:

  • ​<if>​​:条件判断(满足条件才拼接SQL片段);
<where>
  <if test="username != null and username != ''">
    and username like concat('%', #{username}, '%')
  </if>
  <if test="age != null">
    and age = #{age}
  </if>
</where>
  • ​<where>​​​:替代SQL中的​​WHERE​​​关键字,自动去除开头的​​AND/OR​​(避免拼接错误);
  • ​<set>​​​:用于​​UPDATE​​​语句,自动去除结尾的​​,​​(适配动态更新字段);
<update id="updateUser">
  update user
  <set>
    <if test="userName != null">user_name = #{userName},</if>
    <if test="age != null">age = #{age},</if>
  </set>
  where user_id = #{userId}
</update>
  • ​<foreach>​​:遍历集合/数组(如批量查询、批量插入);
<select id="selectByIds" resultType="User">
  select * from user where user_id in
  <foreach collection="ids" item="id" open="(" close=")" separator=",">
    #{id}
  </foreach>
</select>
  • (​​collection​​​:集合参数名;​​item​​​:遍历元素别名;​​open​​​:拼接开头;​​close​​​:拼接结尾;​​separator​​:元素分隔符)
  • ​<choose>​​​ ​​<when>​​​ ​​<otherwise>​​​:类似Java的​​switch-case​​,只执行一个满足条件的片段。

五、缓存机制类

9. MyBatis的一级缓存和二级缓存有什么区别?如何使用二级缓存?

解答
MyBatis缓存是为了减少数据库查询次数,提升性能,分为两级缓存:

对比维度一级缓存(SqlSession级别)二级缓存(Mapper级别)
作用范围单个SqlSession(会话内有效)同一个Mapper接口(跨SqlSession)
存储介质内存(HashMap)内存/第三方缓存(如Redis)
默认状态开启(无法关闭)关闭(需手动开启)
失效场景SqlSession关闭/提交/回滚;执行update/delete/insert操作对应表执行增删改操作;缓存过期;手动清除

二级缓存启用步骤

  1. 全局配置文件开启二级缓存(默认已开启,可省略):
<settings>
  <setting name="cacheEnabled" value="true"/>
</settings>
  1. Mapper.xml中添加​​<cache>​​标签(开启当前Mapper的二级缓存):
<cache eviction="LRU" flushInterval="60000" size="1024" readOnly="true"/>
  • ​eviction​​:缓存回收策略(LRU:最近最少使用,默认;FIFO:先进先出);
  • ​flushInterval​​:缓存过期时间(毫秒);
  • ​size​​:缓存最大条目数;
  • ​readOnly​​:是否只读(true:返回缓存对象本身,性能高;false:返回拷贝对象,安全)。
  1. 实体类需实现​​Serializable​​接口(二级缓存可能序列化存储)。

注意:二级缓存适合查询频繁、修改少的场景;多表关联查询不建议用(缓存失效难以控制)。

六、性能优化类

10. MyBatis的性能优化手段有哪些?

解答

  1. SQL优化
  • 避免​​SELECT *​​,只查询需要的字段;
  • 复杂查询用索引(匹配SQL的WHERE、JOIN条件);
  • 分页查询(用MyBatis分页插件​​PageHelper​​,避免一次性查询大量数据)。
  1. 缓存优化
  • 合理使用二级缓存(查询频繁的Mapper开启);
  • 多表关联查询禁用二级缓存(或手动控制缓存失效);
  • 自定义缓存(如集成Redis,替代默认内存缓存,支持分布式场景)。
  1. 参数与结果映射优化
  • 用​​ResultMap​​​替代​​resultType​​​(解决字段名不一致,避免​​AS​​别名);
  • 大字段(如TEXT)延迟加载(通过​​<result>​​​标签​​fetchType="lazy"​​)。
  1. 执行器优化
  • 批量插入/更新用​​BatchExecutor​​​(需手动设置​​SqlSessionFactory.openSession(ExecutorType.BATCH)​​);
  • 复用Statement用​​ReuseExecutor​​(适合频繁执行相同SQL的场景)。
  1. 连接池优化
  • 整合第三方连接池(如Druid、HikariCP,替代MyBatis默认连接池),配置合理的最大连接数、空闲时间。
  1. 动态SQL优化
  • 避免复杂嵌套的​​<if>​​标签(可读性差,可拆分SQL);
  • 用​​<where>​​​ ​​<set>​​​替代手动拼接​​AND/OR​​​和​​,​​。

七、高级特性类

11. MyBatis的延迟加载(懒加载)是什么?如何实现?

解答
延迟加载是指关联查询时,只查询主表数据,关联表数据在需要时才查询(避免不必要的关联查询,提升性能)。MyBatis默认关闭懒加载,需手动开启。

实现步骤

  1. 全局配置文件开启懒加载:
<settings>
  <setting name="lazyLoadingEnabled" value="true"/> <!-- 开启全局懒加载 -->
  <setting name="aggressiveLazyLoading" value="false"/> <!-- 关闭积极加载(按需加载) -->
</settings>
  1. Mapper.xml中定义关联查询(如一对一​​association​​​、一对多​​collection​​​),设置​​fetchType="lazy"​​:
<resultMap id="UserResultMap" type="User">
  <id column="user_id" property="userId"/>
  <!-- 一对一关联:懒加载订单信息 -->
  <association property="order" column="user_id" select="selectOrderByUserId" fetchType="lazy"/>
</resultMap>

<select id="selectOrderByUserId" resultType="Order">
  select * from order where user_id = #{userId}
</select>

原理:懒加载通过动态代理实现,当访问关联属性(如​​user.getOrder()​​)时,代理对象触发关联SQL的查询。

12. MyBatis如何集成Spring Boot?核心配置是什么?

解答:Spring Boot通过​​mybatis-spring-boot-starter​​简化MyBatis集成,核心步骤:

  1. 引入依赖(Maven):
<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.3.2</version> <!-- 适配Spring Boot版本 -->
</dependency>
<dependency>
  <groupId>com.mysql</groupId>
  <artifactId>mysql-connector-j</artifactId>
  <scope>runtime</scope>
</dependency>
  1. 配置application.yml(核心配置):
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  mapper-locations: classpath:mapper/*.xml # Mapper.xml文件路径
  type-aliases-package: com.example.entity # 实体类别名包(可省略全类名)
  configuration:
    map-underscore-to-camel-case: true # 开启下划线转驼峰(user_name → userName)
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志
  1. 启动类添加​​@MapperScan​​(扫描Mapper接口):
@SpringBootApplication
@MapperScan("com.example.mapper") // Mapper接口所在包
public class MyBatisDemoApplication {
  public static void main(String[] args) {
    SpringApplication.run(MyBatisDemoApplication.class, args);
  }
}
  1. 编写Mapper接口和Mapper.xml(与原生MyBatis一致),直接注入使用:
@Autowired
private UserMapper userMapper;

public User getUserById(Long id) {
  return userMapper.selectById(id);
}

八、问题排查类

13. MyBatis中“Invalid bound statement (not found)”异常的原因有哪些?

解答:该异常是MyBatis最常见异常,核心原因是“Mapper接口与Mapper.xml未正确绑定”,排查方向:

  1. Mapper接口的全类名与Mapper.xml的​​namespace​​不一致;
  2. Mapper接口的方法名与Mapper.xml中​​<select>​​​ ​​<insert>​​​等标签的​​id​​不一致;
  3. Mapper.xml文件路径未配置(Spring Boot中​​mybatis.mapper-locations​​配置错误);
  4. 编译后target目录中没有Mapper.xml文件(Maven项目需在pom.xml中配置资源过滤);
  5. 方法参数类型/返回值类型与Mapper.xml中​​parameterType​​​/​​resultType​​不匹配。
14. MyBatis分页查询时,为什么总返回所有数据(分页失效)?

解答:常见原因:

  1. 未配置分页插件(如PageHelper),或插件配置错误;
  2. 分页插件使用顺序错误(需在查询方法前调用​​PageHelper.startPage(pageNum, pageSize)​​);
  3. 查询方法返回值为​​List​​​,但未用​​Page​​​对象接收(​​Page<User> page = (Page<User>) userMapper.selectAll()​​);
  4. 动态SQL拼接导致分页SQL被覆盖(如​​<where>​​标签未正确闭合)。

总结

MyBatis面试核心围绕“原理+实战+优化”,重点掌握SqlSession工作流程、动态SQL、缓存机制、参数传递、Spring Boot集成等知识点,同时结合实际项目经验,能解释常见异常排查和性能优化场景,即可应对大部分面试场景。