从零开始掌握 MyBatis:一位后端工程师的实战笔记

慢慢写代码
2025-06-14 13:00
阅读 540

背景介绍:为什么我选择 MyBatis?

背景介绍:为什么我选择 MyBatis?

记得刚工作那会儿,我们项目里用的是 Hibernate,一开始觉得它真的挺方便的,几乎不用写 SQL 就能把事情搞定。但随着项目规模变大、业务逻辑越来越复杂,尤其是在做一些高性能查询、动态拼接 SQL 的时候,Hibernate 的问题就开始暴露了。

比如:

  • SQL 难控制:想优化一条慢查询,结果生成的 SQL 根本看不懂。
  • 性能瓶颈:某些复杂的多表查询在 Hibernate 中效率低得可怕。
  • 调试困难:一旦出现 N+1 查询的问题,排查起来特别痛苦。
  • 定制化差:有些业务场景需要自定义字段映射、分页方式等,Hibernate 很难满足。

于是,在一次团队技术选型中,我们决定尝试使用 MyBatis。最初我也只是抱着试试看的心态,没想到这一试,彻底让我爱上了这套框架。

MyBatis 让我把数据库操作牢牢掌控在手中,又能保持代码结构清晰整洁。更重要的是,它对 SQL 友好,适合我们这种对性能有强需求的后端项目。

今天,我就想以一个有过几年实际开发经验的后端工程师身份,结合我在项目中的真实经历,带你一起从头了解 MyBatis 的基础用法,并分享一些我在实际工作中踩过的坑和总结的经验。


项目背景与遇到的问题

项目背景与遇到的问题

我们当时做的是一款内部使用的运营系统,涉及到大量的数据报表展示以及实时数据分析。系统底层采用 MySQL 存储数据,初期用的是 Hibernate,后来随着报表越来越多,页面加载速度明显变慢,部分页面甚至要等个十几秒才能打开。

举个例子,有个“客户消费排行榜”的模块,我们需要联查用户表、订单表、商品分类等多个表,还要支持动态条件过滤(比如时间段、地区、产品类型),同时分页展示。这在 Hibernate 里实现起来非常费劲,而且 SQL 慢到离谱。

为了解决这些问题,我们决定切换成 MyBatis。目标很明确:提升查询效率,简化复杂 SQL 管理,增强可维护性


解决方案:引入 MyBatis 的整体思路

解决方案:引入 MyBatis 的整体思路

MyBatis 最大的特点就是:半自动化 ORM。它不会像 Hibernate 那样替你生成所有 SQL,而是让你自己编写 SQL,然后通过映射文件或注解将 SQL 结果自动封装成 Java 对象。

我们是怎么做的:

  1. 保留部分原有实体类结构

    • 实体类尽量与数据库结构一致
    • 去掉 JPA 的注解,仅保留基本字段名对应
  2. 新建 MyBatis Mapper 接口和 XML 文件

    • 所有查询 SQL 写在 XML 映射文件中,便于集中管理
    • 使用 <select><insert><update><delete> 标签进行 SQL 编写
  3. 统一接口设计规范

    • 所有 DAO 层接口都返回通用封装对象(如 Result
    • 分页统一使用 PageHelper 插件处理
  4. 引入日志插件查看执行 SQL

    • 使用 Log4j + MyBatis-Log4j2 插件查看每次执行的真实 SQL

代码实践:手把手教你写第一个 MyBatis 示例

代码实践:手把手教你写第一个 MyBatis 示例

为了更直观,我带大家一步步实现一个简单的示例:根据用户 ID 查询用户信息

1. 引入依赖(Spring Boot 项目为例)

<!-- MyBatis Starter -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>

<!-- 数据库驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.26</version>
</dependency>

2. 编写实体类 User.java

public class User {
    private Long id;
    private String username;
    private String email;
    private LocalDateTime createdAt;

    // Getter / Setter 省略
}

3. 编写 Mapper 接口 UserMapper.java

@Mapper
public interface UserMapper {
    User selectById(Long id);
}

4. 编写 XML 映射文件 UserMapper.xml

resources/mapper/UserMapper.xml 下创建如下内容:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.mapper.UserMapper">
    <select id="selectById" resultType="com.example.entity.User">
        SELECT id, username, email, created_at
        FROM users
        WHERE id = #{id}
    </select>
</mapper>

5. 配置 MyBatis(application.yml)

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapper/**/*.xml
  type-aliases-package: com.example.entity

完成以上步骤后,就可以在 Service 层注入 UserMapper 并调用了:

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

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

是不是很简单?你可以试着运行一下看看效果,如果能正常返回用户数据,恭喜你!你的第一个 MyBatis 查询就完成了!


踩坑经验:那些年我踩过的 MyBatis 坑

别高兴太早,实际使用过程中,我可是踩了不少坑,下面这些经验希望对你有用。

1. 字段名和属性名不一致导致映射失败

这是一个非常常见的问题。比如数据库字段是 created_at,而 Java 属性是 createdAt。如果不手动配置映射,就会读不到这个字段的数据。

解决方法有两种:

  • 在 XML 中手动写 resultMap:
<resultMap id="BaseResultMap" type="com.example.entity.User">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="email" property="email"/>
    <result column="created_at" property="createdAt"/>
</resultMap>
  • 或者直接开启 MapUnderscoreToCamelCase:
mybatis:
  configuration:
    mapUnderscoreToCamelCase: true

我一般推荐第二种,方便快捷又少出错。


2. XML 文件找不到导致启动失败

这个问题经常出现在刚接触的人身上。Spring Boot 默认不会扫描 src/main/resources 下的 XML 文件,除非你在配置文件中声明路径:

mybatis:
  mapper-locations: classpath:mapper/**/*Mapper.xml

另外注意 @Mapper 注解不要漏加,或者可以配合 @MapperScan 在启动类上统一扫包:

@SpringBootApplication
@MapperScan("com.example.mapper")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3. 缓存导致旧数据没更新

MyBatis 默认二级缓存是关闭的,但如果你开启了,有时会因为缓存导致数据不是最新的。

建议在开发阶段关闭缓存,避免干扰测试:

<configuration>
    <settings>
        <setting name="cacheEnabled" value="false"/>
    </settings>
</configuration>

上线前再根据业务决定是否启用。


效果对比:升级后的系统变化

我们花了大约两周时间把核心模块从 Hibernate 迁移到 MyBatis,迁移后我们做了性能对比:

API接口文档-2

模块 平均响应时间(原) 平均响应时间(新) 提升幅度
客户排行榜 13s 1.2s ~90%
订单汇总报表 7s 0.8s ~88%
用户画像分析 9s 1.5s ~83%

数据库设计模型-1

除了性能大幅提升外,还有几个额外收获:

  • 开发效率更高:可以直接写 SQL,调试也更容易发现问题。
  • 团队协作更顺畅:SQL 和 Java 代码分离,便于多人协作。
  • 可维护性强:遇到慢查询时,可以轻松拿到对应的 SQL 进行优化。

经验总结:几点实用建议

作为一个在后端一线写了几年代码的老兵,我有一些心得想送给即将入门 MyBatis 的你:

✅ 推荐搭配工具链:

  • MyBatis-Plus:功能强大,自带很多便捷方法,尤其适合单表操作
  • PageHelper:分页神器,一行代码就能完成复杂分页逻辑
  • MyBatis Generator:快速生成实体类、Mapper、XML 文件,节省大量重复劳动
  • Druid 或 HikariCP:连接池选择很重要,直接影响并发能力

✅ 关于 SQL 编写习惯:

  • 别把 SQL 全塞进 XML:对于特别复杂的查询,可以考虑拆分成多个 <sql> 片段复用
  • 命名空间要清晰:每个 Mapper 接口对应一张表或一组业务逻辑,避免混用
  • 使用预编译参数 #{} 而非 ${}:防止 SQL 注入风险

✅ 生产环境注意事项:

  • 开启慢查询日志:配合 MySQL 的 slow log,及时发现有问题的 SQL
  • 限制最大连接数:连接池一定要设置合理的最大连接数,避免雪崩
  • 定期清理缓存:如果启用了 MyBatis 的二级缓存,要考虑失效机制

最后一点小感悟:技术永远服务于业务

说到底,MyBatis 只是一个工具。真正的难点在于如何根据业务需求去选择合适的技术方案。我在不同项目中用过 Hibernate、JPA、MyBatis,也写过原生 JDBC,每种方式都有它的适用场景。

MyBatis 是一个平衡点:它不像 Hibernate 那样抽象层太多,也不像纯 JDBC 那样重复劳动太重。它可以让我们写出优雅、高效、可控的 SQL,同时也保有很好的扩展性和维护性。

如果你现在正在做一个需要灵活操作数据库、关注性能的项目,不妨试试 MyBatis。说不定你也会像我一样,一用就爱上它。


作者简介: 一名拥有 5 年经验的后端开发工程师,热爱钻研架构设计与性能优化,擅长使用 Java 技术栈搭建高可用系统。目前任职于某互联网公司负责数据平台的建设与优化。

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝