MyBatis基础教程:Java持久层框架入门实战分享
开篇:为什么选择MyBatis?

还记得我刚入职那会儿,公司项目刚开始从Spring JDBC迁移到一个更灵活的持久层框架。那时候我对各种ORM框架都一知半解,Hibernate、JPA、MyBatis这些词听得耳朵都快起茧了。最后选型MyBatis的原因很实际——我们需要在灵活性和开发效率之间找一个平衡点。
Hibernate确实功能强大,但封装太深;而JPA又偏重于标准规范,对复杂SQL支持有限。最终我们决定采用MyBatis,一款轻量、灵活、适合国内团队使用的持久层框架。
这篇技术文章,就是我想结合自己这几年用MyBatis做项目的经验,写一份“够用、实用、能上手”的入门教程。不是那种官方文档式的东西,而是从真实项目的角度出发,讲清楚为什么要这么用、可能踩什么坑,以及如何高效地搭建起自己的DAO层逻辑。
问题描述:传统数据库操作方式的痛点

在我参与的第一个项目中,我们一开始是直接使用JDBC来操作数据库的。说实话,那段日子真是噩梦。
举个简单的例子:我们要根据用户ID查询用户信息,然后更新他的手机号码。用原生JDBC写的话,得手动处理连接、Statement、ResultSet这些底层细节,代码冗长不说,还容易出错。比如忘关连接啊、参数绑定错了顺序啊,这类低级错误在初期频繁出现。
再加上随着业务增长,SQL越来越复杂,拼接字符串也变得难维护。特别是在做动态条件查询(例如带筛选条件的列表接口)时,用if-else拼字符串简直是在折磨人。
这时候我们开始思考:有没有一种方式既能保留SQL的控制权,又能屏蔽掉底层JDBC的各种琐事?答案当然是肯定的:MyBatis。
解决方案:为什么选择MyBatis?

我们最终选择了MyBatis,主要原因有以下几点:
- 灵活性高:不像Hibernate那样自动帮你生成SQL,MyBatis让我们完全掌控SQL的编写;
- 轻量级易集成:它和Spring Boot整合起来非常方便;
- 性能友好:没有复杂的代理机制,运行效率更高;
- 适合中国开发者习惯:很多后端系统在国内都是以MySQL为核心,MyBatis正好契合这种需求。
接下来我们就通过一个简单的用户管理模块,来看看如何快速上手MyBatis。
代码实践:MyBatis的基础使用步骤
1. 初始化Spring Boot项目
首先创建一个基本的Spring Boot项目,在pom.xml中加入以下依赖:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
如果你还在用旧版本,需要注意升级到兼容Spring Boot 3.x的新版本。
2. 配置数据源和MyBatis
配置文件application.yml内容如下:
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo_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.model
这里要注意mapper-locations的路径是否正确,否则MyBatis会找不到XML映射文件。
3. 定义Model实体类
比如User类:
package com.example.model;
public class User {
private Long id;
private String name;
private String phone;
private String email;
// Getters & Setters
}
记得加上getter/setter方法,这样MyBatis才能正确映射字段。
4. 编写Mapper接口
package com.example.mapper;
import com.example.model.User;
import java.util.List;
public interface UserMapper {
User selectById(Long id);
List<User> selectByCondition(String keyword);
int insert(User user);
int update(User user);
}
这就是DAO接口,方法名对应XML中SQL语句的id。
5. 写XML映射文件
创建src/main/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.model.User">
SELECT * FROM users WHERE id = #{id}
</select>
<select id="selectByCondition" resultType="com.example.model.User">
SELECT * FROM users
<where>
<if test="keyword != null and keyword != ''">
AND (name LIKE CONCAT('%', #{keyword}, '%')
OR phone LIKE CONCAT('%', #{keyword}, '%'))
</if>
</where>
</select>
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (name, phone, email)
VALUES (#{name}, #{phone}, #{email})
</insert>
<update id="update">
UPDATE users
SET name = #{name},
phone = #{phone},
email = #{email}
WHERE id = #{id}
</update>
</mapper>
可以看到,MyBatis的XML提供了强大的标签,如<if>、<choose>等,非常适合构建动态SQL。
6. 在Service中注入并使用
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getUserById(Long id) {
return userMapper.selectById(id);
}
public List<User> searchUsers(String keyword) {
return userMapper.selectByCondition(keyword);
}
}
整个结构清晰、易于维护,而且我们依然可以自由调整每一条SQL。
踩坑经验:那些年我们在MyBatis上栽过的坑

坑一:参数传递方式搞错导致SQL执行异常
比如下面这行代码:
List<User> selectByCondition(String keyword);
而在XML中的if条件是:
<if test="keyword != null and keyword != ''">
没问题对吧?但如果是一个多参数的接口,比如我要传入两个字段怎么办?如果不用@Param指定参数名,MyBatis默认会按顺序取名为arg0、arg1,这时test判断就出错了。
正确做法:
List<User> selectByMultiCondition(@Param("name") String name, @Param("phone") String phone);
然后XML里就可以用:
<if test="name != null">AND name like #{name}</if>
<if test="phone != null">AND phone like #{phone}</if>
这个细节不注意,很容易在调试阶段浪费大量时间。
坑二:自动生成主键没配置好,导致插入失败
在写insert语句的时候,记得添加:
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
否则插入成功后你根本不知道这条记录的实际ID是多少。对于需要返回id的场景(比如后续还要保存关联数据),这就成了一个致命bug。
坑三:XML路径配置错误,导致无法加载映射文件
很多人刚上手MyBatis的时候都会遇到这个问题:提示“No such SQL mapping found”,排查半天发现是因为mapper-locations路径写错了。
建议大家统一放在resources目录下,并且按模块归类,比如:
resources/
└── mapper/
└── user/
└── UserMapper.xml
对应的YAML配置为:
mybatis:
mapper-locations: classpath:mapper/**/*Mapper.xml
效果总结:引入MyBatis后的收益
项目上线后,我们的DAO层代码明显整洁了许多,主要收获有:
- 开发效率提升:减少了重复的JDBC样板代码,提升了70%左右的开发速度;
- 可维护性增强:SQL和业务逻辑分离,修改起来更容易;
- 性能优化空间更大:因为SQL可控,我们可以针对性地进行索引优化或拆分复杂查询;
- 线上问题定位更快捷:可以通过日志输出查看完整的SQL语句,便于排查慢查询等问题。
特别是在线上遇到慢查询时,我可以直接把SQL拿到数据库工具中执行分析,而不是像Hibernate那样只能看HQL。
经验分享:给新手的几点建议

✅ 使用注解还是XML?我建议优先用XML
虽然MyBatis支持使用注解写SQL,但在实际项目中我更推荐使用XML方式。理由是XML结构清晰,尤其在面对复杂SQL、动态条件较多的情况下,阅读和维护成本更低。
✅ 动态SQL尽量模块化,避免过于臃肿
比如 <if>、<choose>标签不要嵌套太多,可以用 <include> 引入公共片段,保持每个SQL块职责单一。
✅ 日志一定要开起来!
调试时强烈建议开启MyBatis的日志输出,比如通过logback配置打印SQL语句:
logging:
level:
com.example.mapper: debug
这样可以在控制台看到完整SQL和参数,对排查问题帮助很大。
技术趋势与生产环境考虑
与Spring Boot的深度整合
目前MyBatis与Spring Boot已经高度集成,几乎做到了即插即用。配合PageHelper可以实现分页查询,结合Druid做数据库监控也很方便。
多数据源处理(进阶)
如果你有多个数据源,比如读写分离或者不同业务库的情况,可以使用dynamic-datasource-spring-boot-starter来管理。这在金融或电商类系统中比较常见。
数据库设计的一些思考
- 表结构要合理设计索引:MyBatis本身不关心索引,但写出来的SQL如果没有命中索引,就会拖慢整体性能。
- 避免大表JOIN过多:适当拆分数据模型,减少单次SQL的复杂度。
小结一下
这篇文章是我结合几个项目的真实经历写成的,希望能让刚接触MyBatis的同学少走一些弯路。总的来说,MyBatis作为Java Web领域最主流的持久层框架之一,它简单易用、性能可控、扩展性强,尤其是在微服务架构下,它是实现轻量级持久化的理想选择。
当然,任何技术都有它的适用场景,我也不是说MyBatis就一定比别的框架好。比如在某些强ORM需求、快速原型搭建的场景中,JPA或Hibernate可能更合适。
但如果你追求对SQL的完全掌控,同时又不想陷入原始JDBC的泥潭,那么MyBatis绝对值得你在项目中试一试。
如果你有任何MyBatis相关的问题,欢迎留言交流。我在工作中也经常被同事拉去帮忙“看看这段SQL为啥查不到”,咱们一起成长、互帮互助 💪。

评论 0