从零开始掌握 MyBatis:一位后端工程师的实战笔记
背景介绍:为什么我选择 MyBatis?

记得刚工作那会儿,我们项目里用的是 Hibernate,一开始觉得它真的挺方便的,几乎不用写 SQL 就能把事情搞定。但随着项目规模变大、业务逻辑越来越复杂,尤其是在做一些高性能查询、动态拼接 SQL 的时候,Hibernate 的问题就开始暴露了。
比如:
- SQL 难控制:想优化一条慢查询,结果生成的 SQL 根本看不懂。
- 性能瓶颈:某些复杂的多表查询在 Hibernate 中效率低得可怕。
- 调试困难:一旦出现 N+1 查询的问题,排查起来特别痛苦。
- 定制化差:有些业务场景需要自定义字段映射、分页方式等,Hibernate 很难满足。
于是,在一次团队技术选型中,我们决定尝试使用 MyBatis。最初我也只是抱着试试看的心态,没想到这一试,彻底让我爱上了这套框架。
MyBatis 让我把数据库操作牢牢掌控在手中,又能保持代码结构清晰整洁。更重要的是,它对 SQL 友好,适合我们这种对性能有强需求的后端项目。
今天,我就想以一个有过几年实际开发经验的后端工程师身份,结合我在项目中的真实经历,带你一起从头了解 MyBatis 的基础用法,并分享一些我在实际工作中踩过的坑和总结的经验。
项目背景与遇到的问题

我们当时做的是一款内部使用的运营系统,涉及到大量的数据报表展示以及实时数据分析。系统底层采用 MySQL 存储数据,初期用的是 Hibernate,后来随着报表越来越多,页面加载速度明显变慢,部分页面甚至要等个十几秒才能打开。
举个例子,有个“客户消费排行榜”的模块,我们需要联查用户表、订单表、商品分类等多个表,还要支持动态条件过滤(比如时间段、地区、产品类型),同时分页展示。这在 Hibernate 里实现起来非常费劲,而且 SQL 慢到离谱。
为了解决这些问题,我们决定切换成 MyBatis。目标很明确:提升查询效率,简化复杂 SQL 管理,增强可维护性。
解决方案:引入 MyBatis 的整体思路

MyBatis 最大的特点就是:半自动化 ORM。它不会像 Hibernate 那样替你生成所有 SQL,而是让你自己编写 SQL,然后通过映射文件或注解将 SQL 结果自动封装成 Java 对象。
我们是怎么做的:
保留部分原有实体类结构:
- 实体类尽量与数据库结构一致
- 去掉 JPA 的注解,仅保留基本字段名对应
新建 MyBatis Mapper 接口和 XML 文件:
- 所有查询 SQL 写在 XML 映射文件中,便于集中管理
- 使用
<select>、<insert>、<update>、<delete>标签进行 SQL 编写
统一接口设计规范:
- 所有 DAO 层接口都返回通用封装对象(如 Result
) - 分页统一使用 PageHelper 插件处理
- 所有 DAO 层接口都返回通用封装对象(如 Result
引入日志插件查看执行 SQL:
- 使用 Log4j + MyBatis-Log4j2 插件查看每次执行的真实 SQL
代码实践:手把手教你写第一个 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,迁移后我们做了性能对比:

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

除了性能大幅提升外,还有几个额外收获:
- 开发效率更高:可以直接写 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