MyBatis基础教程:Java持久层框架入门
MyBatis 基础实战:从 JDBC 到 ORM 的跨越之路

开篇:为什么选择 MyBatis?
我是一名有 5 年后端开发经验的 Java 工程师,经历过多个中小型项目的迭代与重构。在刚开始工作的那一年,我和很多新人一样,都是直接写 JDBC 操作数据库的代码。后来接触到了 Hibernate 这样的全自动化 ORM 框架,当时觉得“哇,终于不用自己拼 SQL 了!”但没过多久就发现,在实际项目中,Hibernate 的灵活性和性能表现并不总是那么令人满意。
真正让我转变观念的是第一次接触 MyBatis。那时候团队要做一个数据中台服务,核心是打通几个业务系统的数据接口,要求高效、灵活地操作数据库。起初我还抱着 Hibernate 不放,结果在多表关联、复杂查询、分页优化等场景下频繁碰壁。最终我们选择了 MyBatis 作为持久层框架,没想到这一用就是这几年来最顺手的一个技术选型。
今天这篇文章想结合我个人使用 MyBatis 的真实经历,给大家分享一下这个框架的基础入门心得,也会穿插一些我踩过的坑和总结的经验。
项目背景:一个典型的中台系统需求
我们当时的项目是一个公司级的数据汇聚平台,主要职责是把 CRM、ERP、WMS 等系统的部分核心数据汇总起来,对外暴露统一的 RESTful 接口供前端和其他后台调用。
数据结构方面比较复杂,涉及大量的 JOIN 查询和跨库映射。同时由于是面向多个系统的聚合查询,对性能要求也很高,不能接受那种“一句查询执行好几秒”的情况。
遇到的挑战:传统方式 vs ORM 的尴尬
初期尝试用纯 JDBC 去实现数据访问层,结果很快出现了以下问题:
- SQL 分散在各个 DAO 类中,可维护性差;
- 手动处理 ResultSet 映射繁琐,容易出错;
- 多条件组合查询时,SQL 字符串拼接难以管理;
- 数据源连接池、事务控制都需要自己封装,容易出现并发问题。
切换到 Hibernate 后虽然简化了很多 CRUD 操作,但也暴露出新问题:
- 自动生成的 SQL 性能不佳,特别是 LEFT JOIN 场景;
- 对于动态查询支持较弱,需要借助 Criteria API 写法不够直观;
- 有些复杂报表查询无法通过实体类模型表达;
- 联合主键和非规范化设计的表结构导致映射困难。
这时,我们开始研究其他替代方案,MyBatis 就是在这个背景下进入视野的。
解决思路:MyBatis 的“中间派”优势
MyBatis 是介于原生 JDBC 和全自动 ORM(如 Hibernate/JPA)之间的半自动化 ORM 框架。它不强制你完全使用对象模型来映射数据库表,而是保留了 SQL 的自由度,同时又屏蔽了底层操作,比如连接获取、参数绑定、结果集处理、事务管理等。
我们选择 MyBatis 主要是看中了它的以下几个特点:
- SQL 完全掌控:你可以自己写 SQL,适合复杂业务场景;
- 轻量级配置:比起 Spring Data JPA,MyBatis 上手更快;
- 灵活的映射机制:可以通过 XML 或注解定义 SQL 和结果集映射;
- 良好的社区生态:Spring Boot 支持非常成熟,集成简单;
- 易于调试与优化:可以直接看到发出的 SQL,方便 DBA 协助调优。

代码实践:快速上手 MyBatis
为了让大家有个直观的认识,下面是一段简单的示例代码。
添加依赖(以 Maven 为例)
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
</dependencies>

配置数据源(application.yml)
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?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.model
编写 Mapper 接口
@Mapper
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User selectById(Long id);
List<User> selectByCondition(@Param("name") String name, @Param("age") Integer age);
}
对应的 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="selectByCondition" resultType="com.example.model.User">
SELECT * FROM users
<where>
<if test="name != null">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="age != null">
AND age >= #{age}
</if>
</where>
</select>
</mapper>
是不是感觉比 Hibernate 更加“接地气”?尤其是 <where> 和 <if> 标签的配合使用,让动态 SQL 清晰又可控。
踩坑经验:那些年我们一起绕过的弯路
1. 忘记开启自动提交或事务回滚
有一次在一个批量导入逻辑中,插入几千条记录失败后没有进行回滚,结果造成脏数据。后来我们在 Service 层添加了 @Transactional 注解,并且显式设置了事务边界。
@Transactional
public void importData(List<User> userList) {
for (User user : userList) {
userMapper.insert(user);
}
}
2. 结果集映射错误导致字段丢失
早期经常因为字段名和属性名不一致导致某些字段始终为 null。后来在 resultMap 中手动指定映射关系,或者直接开启驼峰命名转下划线自动转换功能:
mybatis:
configuration:
mapUnderscoreToCamelCase: true
3. 误用 $ 而不是 #,引发 SQL 注入风险
这是一个很经典的坑。例如:
-- 错误用法
SELECT * FROM users WHERE name = '${name}'
-- 正确做法
SELECT * FROM users WHERE name = #{name}
#{} 会预编译防止注入,${} 是字符串替换,容易被攻击。
效果总结:项目上线后的变化
引入 MyBatis 后,整个项目的数据库操作模块更加清晰,接口响应速度平均提升了 30% 以上。特别是在一些复杂的 JOIN 查询、报表统计场景中,可以自由编写 SQL,避免了 Hibernate 生成低效语句的问题。
同时,SQL 与 Java 代码分离的设计也便于 DBA 协助优化查询语句。我们在后期还结合了 Druid 监控,实时查看慢 SQL,进一步提升了系统稳定性。
我的经验建议
不要迷信 ORM 框架,要懂得权衡
Hibernate 很强大,但在复杂查询面前显得力不从心。而 MyBatis 提供了一种平衡的选择。SQL 没有想象中那么简单,要学会写优雅的 SQL
特别是面对大数据量时,索引、JOIN 顺序、子查询等都会影响性能。有时候优化一条 SQL,胜过十次 Java 层面的优化。合理使用动态 SQL 和 resultMap
动态 SQL 是 MyBatis 的灵魂之一,学会灵活运用<if>、<choose>、<set>等标签,可以大大减少代码冗余。搭配日志工具追踪 SQL,提升 Debug 效率
推荐使用 MyBatis Log Plugin 插件,在 IDE 里就可以直接看到拼好的 SQL,极大方便调试。生产环境务必做好监控和调优
比如使用 Druid、SkyWalking 等工具对 SQL 执行时间、连接池状态进行监控,及时发现问题。
写在最后:适合自己的才是最好的
MyBatis 不是万能的,也不是唯一的选择。在我参与的微服务架构项目中,也遇到过使用 JPA + QueryDSL 的团队,他们的做法更适合严格的领域模型驱动开发。但我相信在大多数企业级 Java 后端项目中,MyBatis 依然有着不可替代的优势。
作为一个开发者,我觉得最重要的是理解每一项技术背后的原理和适用场景。不要盲目追求“最新最热”,也不要一成不变地守着老一套。很多时候,选型没有绝对正确,只有更合适。
希望这篇结合了我个人实战经验的文章,能够帮助大家更好地理解和使用 MyBatis。如果你也有类似的踩坑经历或者不同看法,欢迎留言交流,共同成长!

评论 0