MyBatis 面试高频题及答案详解(附实战案例)

MyBatis 面试高频题及答案详解(附实战案例)

MyBatis 作为 Java 开发中广泛使用的持久层框架,因其灵活的 SQL 控制能力和高效的性能优化策略而备受青睐。本文基于实际面试场景,整理了 MyBatis 的高频面试题及详细解析,涵盖核心概念、动态 SQL、缓存机制、高级功能和实际应用案例,帮助开发者全面掌握 MyBatis 的核心知识点。


一、MyBatis 基础概念与核心功能

1. MyBatis 的核心配置文件是什么?其作用是什么?

答案

  • 核心配置文件mybatis-config.xml
  • 作用
    • 配置 MyBatis 的运行环境,如数据库连接信息(数据源、事务管理器)、映射文件路径、类型别名等。
    • 定义默认的数据库环境(通过 <environments> 标签的 default 属性)。

实战案例

<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="UserMapper.xml"/>
  </mappers>
</configuration>

2. MyBatis 中 #{} ${} 的区别是什么?

答案

  • #{}
    • 预编译处理,防止 SQL 注入。
    • 适用于参数替换,如 SELECT * FROM users WHERE id = #{id}
  • ${}
    • 字符串拼接,直接替换变量值,存在 SQL 注入风险。
    • 适用于动态列名或表名,如 ORDER BY ${column}

实战案例

-- 安全查询
SELECT * FROM users WHERE name = #{name};

-- 模糊查询(需注意安全)
SELECT * FROM users WHERE name LIKE '%${keyword}%';

二、MyBatis 查询与结果映射

3. MyBatis 如何实现多表关联查询?

答案

  • 方式

    • 嵌套查询:通过 <association><collection> 标签实现一对一、一对多关联。
    • 联合查询:通过 JOIN 语句一次性查询所有数据。
  • 实现原理

    • 嵌套查询:先查询主表数据,再根据主表字段值查询关联表(延迟加载可减少数据库压力)。
    • 联合查询:通过 <resultMap> 映射关联字段,避免多次查询。

实战案例

<!-- 一对一关联 -->
<resultMap id="userResultMap" type="User">
  <id property="id" column="id"/>
  <result property="name" column="name"/>
  <association property="role" column="role_id" javaType="Role" 
               select="selectRoleById"/>
</resultMap>

<select id="selectUserById" resultMap="userResultMap">
  SELECT * FROM users WHERE id = #{id};
</select>

<select id="selectRoleById" resultType="Role">
  SELECT * FROM roles WHERE id = #{id};
</select>

4. MyBatis 的 resultTyperesultMap 有什么区别?

答案

  • resultType
    • 自动匹配数据库列名与实体类属性名(大小写不敏感)。
    • 适用于简单映射场景。
  • resultMap
    • 自定义列与属性的映射关系,支持复杂映射(如嵌套对象、集合)。
    • 推荐用于多表关联或字段名不一致的场景。

实战案例

<!-- 使用 resultMap 映射复杂字段 -->
<resultMap id="orderResultMap" type="Order">
  <id property="orderId" column="order_id"/>
  <result property="productName" column="product_name"/>
  <collection property="items" ofType="OrderItem" 
              column="order_id" select="selectItemsByOrderId"/>
</resultMap>

三、MyBatis 动态 SQL 与性能优化

5. MyBatis 的动态 SQL 有哪些标签?如何使用?

答案

  • 常用标签
    • <if>:条件判断(如 WHERE name = #{name})。
    • <choose>/<when>/<otherwise>:分支选择。
    • <foreach>:循环遍历(如批量插入、IN 查询)。
    • <set>:更新操作时自动去除末尾逗号。
    • <where>:动态生成 WHERE 子句。

实战案例

<select id="searchUsers" resultType="User">
  SELECT * FROM users
  <where>
    <if test="name != null">
      AND name LIKE '%${name}%'
    </if>
    <if test="age != null">
      AND age > #{age}
    </if>
  </where>
</select>

6. MyBatis 如何实现分页查询?

答案

  • 方式
    • RowBounds 分页:通过限制起始行和行数实现(简单但不推荐,无法拦截 SQL)。
    • PageHelper 插件:自动拦截 SQL,添加 LIMITOFFSET 子句。

实战案例

// 使用 PageHelper 插件
PageHelper.startPage(1, 10);
List<User> users = userMapper.selectAll();
PageInfo<User> pageInfo = new PageInfo<>(users);

四、MyBatis 缓存机制与性能调优

7. MyBatis 的一级缓存和二级缓存有什么区别?

答案

  • 一级缓存(SqlSession 级别)
    • 默认开启,生命周期与 SqlSession 一致。
    • 适用于单次会话内的重复查询。
  • 二级缓存(Mapper 级别)
    • 需手动配置,生命周期与 Mapper 关联。
    • 适用于多会话共享的高频查询数据。

实战案例

<!-- 启用二级缓存 -->
<cache eviction="FIFO" size="1024"/>

<!-- 配置二级缓存 -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

8. 如何防止 MyBatis 中的 SQL 注入?

答案

  • 核心方法
    • 使用 #{} 替代 ${},避免用户输入直接拼接到 SQL 中。
    • 对用户输入进行校验和过滤(如白名单机制)。
    • 结合数据库的预编译功能(如 PreparedStatement)。

实战案例

-- 安全写法
SELECT * FROM users WHERE name = #{name};

-- 不安全写法(避免使用)
SELECT * FROM users WHERE name = '${name}';

五、MyBatis 高级功能与插件开发

9. 如何实现 MyBatis 的延迟加载?

答案

  • 原理
    • 使用 CGLIB 创建代理对象,当访问关联对象时触发额外查询。
    • 通过 lazyLoadEnabled 配置启用。

实战案例

<settings>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="aggressiveLazyLoading" value="false"/>
</settings>

10. 如何编写 MyBatis 插件?

答案

  • 步骤
    1. 实现 Interceptor 接口并重写 intercept() 方法。
    2. 通过 @Intercepts 注解指定拦截的目标方法(如 Executor.query())。
    3. 在配置文件中注册插件。

实战案例

@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class})})
public class MyPlugin implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 在 SQL 执行前后添加逻辑
        return invocation.proceed();
    }
}

六、MyBatis 与 Spring 集成

11. MyBatis 与 Spring 如何集成?

答案

  • 核心组件
    • SqlSessionFactoryBean:创建 SqlSessionFactory
    • MapperScannerConfigurer:自动扫描 Mapper 接口。
  • 配置方式
    • 通过 XML 配置或 Java 注解(如 @MapperScan)。

实战案例

@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        return factory.getObject();
    }
}

七、MyBatis 实际应用场景与案例

12. 在电商平台中,如何使用 MyBatis 实现商品分类的树形结构查询?

答案

  • 实现思路
    1. 查询所有分类数据,并按父类 ID 建立父子关系。
    2. 使用递归算法(如深度优先搜索)构建树形结构。

实战案例

public List<Category> buildCategoryTree(List<Category> categories) {
    Map<Long, Category> categoryMap = new HashMap<>();
    for (Category category : categories) {
        categoryMap.put(category.getId(), category);
    }

    List<Category> rootCategories = new ArrayList<>();
    for (Category category : categories) {
        if (category.getParentId() == 0) {
            rootCategories.add(category);
        } else {
            Category parent = categoryMap.get(category.getParentId());
            if (parent != null) {
                parent.getChildren().add(category);
            }
        }
    }
    return rootCategories;
}

八、总结

MyBatis 以其灵活的 SQL 控制能力和高效的性能优化策略,成为 Java 开发中不可或缺的持久层框架。通过掌握其核心概念(如动态 SQL、缓存机制)、高级功能(如插件开发、延迟加载)以及实际应用技巧(如多表关联查询、分页优化),开发者可以高效应对复杂的业务需求。在实际面试中,结合具体场景和代码示例进行解析,能够更直观地展示对 MyBatis 的深入理解与应用能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷爱码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值