MyBatis 到底能帮你省下多少 JDBC 的重复代码?
大家好,我是小李,一个从培训班“毕业”、如今在一线做后端开发的普通程序员。还记得我当初学 Java Web 开发时,被 JDBC 折磨得死去活来——写个查询要处理 Connection、Statement、ResultSet,还要手动关资源,一不小心就内存泄漏。更别提 SQL 和 Java 代码混在一起,改个字段恨不得重写半页代码。
后来接触到 MyBatis,我才真正体会到什么叫“解放生产力”。今天,我就以一个过来人的身份,带完全零基础的朋友,手把手入门这个超实用的 Java 持久层框架。不讲虚的,只用最简单的语言 + 最贴近实战的例子,让你今天就能跑通第一个 MyBatis 程序。
更重要的是,文章末尾我还加了几个高频面试题挑战,帮你提前感受真实求职场景。准备好了吗?我们开始!
为什么你需要 MyBatis?
简单说:MyBatis 是一个帮助 Java 程序和数据库“友好对话”的工具。
想象一下:你的 Java 程序想查用户信息,但数据库只会听 SQL。传统方式(JDBC)要求你:
- 手动加载驱动
- 手动获取连接
- 手动拼接 SQL 字符串(容易出错!)
- 手动把 ResultSet 转成 Java 对象
- 手动关闭所有资源
而 MyBatis 做了这些事:
✅ 自动管理数据库连接
✅ 把 SQL 写在 XML 或注解里(清晰分离)
✅ 自动把查询结果映射成 Java 对象(不用再写一堆 set/get)
✅ 支持动态 SQL(比如根据条件拼 WHERE)
一句话总结:MyBatis 让你专注业务逻辑,而不是重复造轮子。
第一步:搭建开发环境(别跳过!)
💡 提示:以下步骤我当初踩过坑,务必按顺序操作!
所需工具清单
| 工具 | 版本建议 | 说明 |
|---|---|---|
| JDK | 8 或 11 | Java 运行环境 |
| Maven | 3.6+ | 项目依赖管理(推荐) |
| IDE | IntelliJ IDEA / Eclipse | 推荐 IDEA,对 MyBatis 支持更好 |
| 数据库 | MySQL 5.7+ | 本文以 MySQL 为例 |
创建 Maven 项目
打开终端或 IDE,执行:
mvn archetype:generate -DgroupId=com.example -DartifactId=mybatis-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
进入项目目录,编辑 pom.xml,添加核心依赖:
<dependencies>
<!-- MyBatis 核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- 单元测试(方便验证) -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
⚠️ 注意:如果你用 MySQL 8.0+,记得在连接字符串里加
?serverTimezone=UTC,否则会报时区错误!
核心概念:别被术语吓到
MyBatis 就像一个“翻译官”,它需要知道三件事:
- 怎么连数据库? → 配置文件 (
mybatis-config.xml) - SQL 写在哪? → 映射文件 (
UserMapper.xml) 或 注解 - 查出来的数据变成啥对象? → Java Bean(比如
User.java)
下面逐个拆解。
1. 配置文件:mybatis-config.xml
放在 src/main/resources 目录下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="your_password"/>
</dataSource>
</environment>
</environments>
<!-- 告诉 MyBatis 去哪找 SQL 映射文件 -->
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
✅ 小贴士:
POOLED表示使用连接池,比每次新建连接高效得多。
2. Java Bean:User.java
package com.example;
public class User {
private int id;
private String name;
private String email;
// 必须有无参构造器(MyBatis 反射需要)
public User() {}
// Getter 和 Setter
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
}
}
3. 映射文件:UserMapper.xml
同样放在 resources 目录:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.UserMapper">
<!-- id 对应接口方法名,resultType 是返回类型 -->
<select id="findById" resultType="com.example.User">
SELECT id, name, email FROM users WHERE id = #{id}
</select>
</mapper>
🔍 注意:
#{id}是安全的占位符,会自动防 SQL 注入!
实战:5 分钟写出第一个查询
我们来实现一个功能:根据 ID 查询用户。
步骤 1:创建数据库表
CREATE DATABASE testdb;
USE testdb;
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
email VARCHAR(100)
);
INSERT INTO users (name, email) VALUES
('张三', 'zhangsan@example.com'),
('李四', 'lisi@example.com');
步骤 2:编写测试代码
在 src/test/java/com/example/MyBatisTest.java 中:
package com.example;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.InputStream;
public class MyBatisTest {
@Test
public void testFindById() throws Exception {
// 1. 读取配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 2. 创建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3. 获取 SqlSession(相当于 JDBC 的 Connection)
try (SqlSession session = sqlSessionFactory.openSession()) {
// 4. 执行 SQL:namespace.id
User user = session.selectOne("com.example.UserMapper.findById", 1);
System.out.println(user); // 输出:User{id=1, name='张三', email='zhangsan@example.com'}
}
}
}
运行这个测试,如果看到控制台打印出用户信息,恭喜你!MyBatis 已经跑起来了!
更优雅的方式:使用 Mapper 接口
上面的方式虽然能用,但字符串 "com.example.UserMapper.findById" 容易写错。MyBatis 推荐用 接口 + 注解/映射文件 的方式。
1. 创建接口
package com.example;
public interface UserMapper {
User findById(int id);
}
2. 修改 UserMapper.xml
只需把 namespace 改成接口全路径:
<mapper namespace="com.example.UserMapper">
<select id="findById" resultType="com.example.User">
SELECT id, name, email FROM users WHERE id = #{id}
</select>
</mapper>
3. 修改测试代码
@Test
public void testWithMapper() throws Exception {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
// 获取 Mapper 接口的代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.findById(1);
System.out.println(user);
}
}
现在,IDE 能自动提示方法名,再也不怕拼错!这就是 面向接口编程 的好处。
动态 SQL:条件查询也不怕
实际开发中,经常要根据条件查询。比如:“如果传了名字就按名字查,否则查全部”。
MyBatis 提供 <if>、<where> 等标签:
<select id="findByCondition" resultType="com.example.User">
SELECT id, name, email FROM users
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="email != null and email != ''">
AND email = #{email}
</if>
</where>
</select>
对应的接口方法:
List<User> findByCondition(@Param("name") String name, @Param("email") String email);
调用示例:
List<User> users = mapper.findByCondition("张", null); // 查名字含“张”的用户
✅
@Param注解用于给参数命名,XML 中用#{name}引用。
新手常见问题 & 避坑指南
❌ 问题1:ClassNotFoundException: Cannot find driver
原因:没加 MySQL 驱动依赖,或版本不对。
解决:检查 pom.xml 是否包含 mysql-connector-java,且 scope 不是 provided。
❌ 问题2:Property 'dataSource' is required
原因:mybatis-config.xml 中 <dataSource> 配置缺失或拼写错误。
解决:仔细核对 driver、url、username、password 四项。
❌ 问题3:Invalid bound statement (not found)
原因:Mapper 接口方法名和 XML 中的 id 不一致,或 namespace 写错。
解决:确保三者一致:
- 接口方法名 = XML 中
<select id="xxx"> - namespace = 接口全限定名
❌ 问题4:中文乱码
解决:在 JDBC URL 后加上 &useUnicode=true&characterEncoding=utf8:
jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
面试题挑战:看看你能答对几道?
💡 这些都是我面试时被问过的真实题目!
题目1:MyBatis 中 #{} 和 ${} 有什么区别?
#{}:预编译处理,会转成?占位符,自动防 SQL 注入。${}:字符串替换,直接拼接到 SQL 中,有注入风险,仅用于动态表名、列名等场景。
✅ 正确用法:WHERE id = #{id}
❌ 危险用法:WHERE id = ${id}(若 id 是用户输入,可能被注入)
题目2:MyBatis 如何实现分页?
两种方式:
- 物理分页:在 SQL 中加
LIMIT offset, size(推荐)<select id="findAll" resultType="User"> SELECT * FROM users LIMIT #{offset}, #{size} </select> - 逻辑分页:先查全部,再在 Java 中截取(数据量大时不推荐)
📌 高级方案:集成 PageHelper 插件,一行代码自动分页。
题目3:什么是 Embedding?MyBatis 支持吗?
注意!这里有个陷阱——Embedding(嵌入)通常指 AI 领域的向量表示,和 MyBatis 无关。
但在数据库上下文中,有人误以为“嵌套查询”叫 Embedding。实际上,MyBatis 支持:
- 关联查询(一对一、一对多)通过
<association>和<collection> - 嵌套结果映射
例如:查询用户及其订单:
<resultMap id="UserWithOrders" type="User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<collection property="orders" ofType="Order">
<id property="id" column="order_id"/>
<result property="amount" column="amount"/>
</collection>
</resultMap>
所以,面试时如果听到“Embedding”,先确认上下文!大概率是面试官口误,实际想问“嵌套查询”或“关联映射”。
下一步学习建议
- 掌握 CRUD 全套操作:增删改查都要动手写一遍
- 学习缓存机制:MyBatis 有一级缓存(SqlSession 级)、二级缓存(Mapper 级)
- 尝试注解方式:
@Select,@Insert等,适合简单 SQL - 集成 Spring Boot:实际项目几乎都用
mybatis-spring-boot-starter - 阅读官方文档:https://mybatis.org/mybatis-3/zh/
🌟 最后提醒:不要死记硬背!每个概念都自己敲代码验证。我当初就是靠“边学边删边重写”才真正掌握的。
结语
MyBatis 并不难,难的是迈出第一步。希望这篇教程能帮你绕过我当年踩过的坑。记住:所有复杂的框架,底层都是为了解决简单的问题。
现在,打开你的 IDE,创建那个 mybatis-demo 项目吧!跑通第一个查询的那一刻,你会感谢今天的自己。
加油,未来的 Java 工程师!

评论 0