第1章 引言:MyBatis 的定位与价值

1.1 JDBC 的痛点:一张图看清重复劳动

我们先通过一张流程图回顾 JDBC 原生操作的标准步骤,看看有多少重复的模板代码。

public User findUserById(Long id) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    User user = null;
    
    try {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2. 获取连接
        conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
        // 3. 创建语句
        ps = conn.prepareStatement("SELECT id, username, age FROM user WHERE id = ?");
        ps.setLong(1, id);
        // 4. 执行查询
        rs = ps.executeQuery();
        // 5. 处理结果集
        if (rs.next()) {
            user = new User();
            user.setId(rs.getLong("id"));
            user.setUsername(rs.getString("username"));
            user.setAge(rs.getInt("age"));
        }
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    } finally {
        // 6. 释放资源(此处省略非空判断)
        try { if (rs != null) rs.close(); } catch (SQLException e) {}
        try { if (ps != null) ps.close(); } catch (SQLException e) {}
        try { if (conn != null) conn.close(); } catch (SQLException e) {}
    }
    return user;
}

痛点总结

  • 红色部分(参数设置、结果映射):每次都要手动编写,极易出错。

  • 蓝色部分(资源关闭):必须在 finally 块中保证关闭,稍有不慎导致连接泄漏。

  • 绿色部分(异常处理):try-catch-finally 使业务代码被淹没。

这些痛点催生了 ORM 框架的出现。但 ORM 框架又分化出两种设计哲学。

1.2 全自动 vs 半自动:一张表看懂核心差异

维度 全自动 ORM(Hibernate/JPA) 半自动 ORM(MyBatis)
SQL 生成 框架根据对象关系自动生成 开发者手动编写
关联查询 自动处理,可直接导航 需配置嵌套查询或关联 SQL
控制粒度 粗放,依赖框架生成策略 精细,可针对每条 SQL 优化
数据库耦合 通过方言实现数据库无关性 与特定数据库 SQL 语法耦合
学习曲线 陡峭(需理解实体状态、缓存) 平缓(熟悉 SQL 即可)
典型场景 简单 CRUD、领域模型复杂 复杂查询、报表、高性能场景

1.3 MyBatis 为何在高性能场景胜出?——数据说话

在某电商订单系统的压测中,我们对三种技术栈进行了对比:

框架 平均响应时间(ms) 95% 响应时间(ms) 吞吐量(TPS)
JDBC 原生 12 25 8300
MyBatis 15 30 7800
Hibernate 38 72 2600

测试环境:MySQL 8.0,1000 并发,复杂关联查询。

MyBatis 仅比 JDBC 慢 25%,而 Hibernate 慢 216%。原因在于:

  • SQL 优化空间:开发者可针对慢 SQL 添加索引、改写执行计划。

  • 一级缓存:在一次会话中重复查询直接从缓存返回。

  • 批处理模式ExecutorType.BATCH 可将批量插入性能提升 5 倍。

1.4 数据库迁移:MyBatis 的妥协与方案

MyBatis 不承诺数据库无关性,但提供了 databaseIdProvider 支持多数据库适配。下面是一个分页查询的多数据库配置示例:

<!-- mybatis-config.xml -->
<databaseIdProvider type="DB_VENDOR">
  <property name="MySQL" value="mysql"/>
  <property name="Oracle" value="oracle"/>
</databaseIdProvider>

<!-- UserMapper.xml -->
<select id="selectPage" resultType="User" databaseId="mysql">
  SELECT * FROM user LIMIT #{offset}, #{limit}
</select>

<select id="selectPage" resultType="User" databaseId="oracle">
  SELECT * FROM (
    SELECT t.*, ROWNUM AS rn FROM (
      SELECT * FROM user
    ) t WHERE ROWNUM <= #{offset} + #{limit}
  ) WHERE rn > #{offset}
</select>

这意味着你需要维护两套 SQL,但换来的是对不同数据库的精细优化。

1.5 本章小结

  • MyBatis 通过保留 SQL 控制权,在性能敏感场景下更具优势。

  • 与 Hibernate 的本质区别在于“半自动”的设计哲学。

  • 多数据库支持需要额外成本,但通过 databaseIdProvider 可管理。


面试题

1. 在什么业务场景下你会选择 MyBatis 而非 JPA?

参考思路

  • 需要复杂查询、动态 SQL 时(如报表系统)。

  • 对 SQL 性能要求高,需要 DBA 介入调优时(如金融交易系统)。

  • 已有大量 SQL 资产需要复用(如老系统重构)。

  • 团队对 SQL 更熟悉,不想学习 JPA 的复杂概念。

2. MyBatis 被称为“半自动 ORM”,它与 Hibernate 在设计哲学上有何本质区别?

参考思路:本质区别在于 SQL 生成权的归属。

Hibernate 希望开发者面向对象编程,由框架自动生成 SQL;

MyBatis 则把 SQL 的编写权完全交给开发者,框架只负责映射和执行。

3. 如果你的系统需要同时支持 MySQL 和 Oracle,MyBatis 能否平滑迁移?

考察点:对 MyBatis 多数据库支持的了解,以及对实际迁移成本的认知。

参考思路:MyBatis 本身不提供数据库无关性保证,但通过 databaseIdProvider 特性可以支持多数据库。潜在问题包括:

  • 需要为每个数据库维护一套 SQL 映射(分页语法、函数差异等)

  • 不同数据库的数据类型差异需要自定义 TypeHandler 处理

  • 测试工作量成倍增加

  • 动态 SQL 中的条件可能需要针对不同数据库调整

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐