MyBatis 到底能帮你省下多少 Java 数据库代码?
大家好,我是小张,一名 211 高校的计算机专业研究生。平时除了啃论文、调模型,我最大的爱好就是写技术博客——尤其是给刚入门的同学“搭梯子”。今天这篇教程,就是源于我当初学 Java 后端开发时踩过的一个大坑:怎么把 Java 对象存到数据库里?
那时候我以为要用纯 JDBC 手写一堆 PreparedStatement 和 ResultSet,结果代码又臭又长,还容易出错。后来导师一句话点醒我:“试试 MyBatis。” 从此,我的持久层代码清爽了至少一半。
所以,如果你也正在为 Java 操作数据库而头疼,这篇《MyBatis 基础教程》就是为你量身打造的。零基础也能跟着敲完一个完整示例,而且我会用最直白的语言解释每个概念,绝不堆术语。
注:虽然标题提到了“算法”,但 MyBatis 本身不涉及复杂算法——它更像一个“智能翻译官”,把 Java 方法自动转成 SQL 语句。不过,理解它的底层映射机制,其实也是一种轻量级的“对象-关系映射算法”思维。
一、MyBatis 是什么?为什么你需要它?
简单说:MyBatis 是一个 Java 持久层框架,专门用来简化数据库操作。
想象一下,你有一个 User 类:
public class User {
private int id;
private String name;
private int age;
// getter/setter 省略
}
你想把用户信息存进 MySQL 的 users 表。不用框架的话,得这样写:
String sql = "INSERT INTO users(name, age) VALUES (?, ?)";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, user.getName());
ps.setInt(2, user.getAge());
ps.executeUpdate();
查数据更麻烦,还得手动把 ResultSet 一行行转成 Java 对象。
而 MyBatis 能让你只写接口和 SQL,剩下的它全包了。比如:
@Select("SELECT * FROM users WHERE id = #{id}")
User findById(int id);
是不是清爽多了?
💡 核心价值:MyBatis 把你从繁琐的 JDBC 模板代码中解放出来,让你专注业务逻辑和 SQL 本身。
二、环境准备:5 分钟搭建开发环境
我们用最主流的组合:IntelliJ IDEA + Maven + MySQL + JDK 8+。
步骤 1:创建 Maven 项目
打开 IDEA → New Project → 选择 Maven → 不勾选 archetype → 项目名比如 mybatis-demo。
步骤 2:添加依赖(pom.xml)
在 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>
⚠️ 注意:如果你用的是 MySQL 8.x,驱动类名是
com.mysql.cj.jdbc.Driver,不是旧版的com.mysql.jdbc.Driver。
步骤 3:创建数据库和表
打开你的 MySQL 客户端(如 Navicat 或命令行),执行:
CREATE DATABASE mybatis_demo;
USE mybatis_demo;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
age INT NOT NULL
);
步骤 4:配置 MyBatis(mybatis-config.xml)
在 src/main/resources 下新建 mybatis-config.xml:
<?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/mybatis_demo?useSSL=false&serverTimezone=UTC"/>
<property name="username" value="你的用户名"/>
<property name="password" value="你的密码"/>
</dataSource>
</environment>
</environments>
<!-- 后面会在这里注册 Mapper -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
🔒 安全提示:生产环境中密码不要硬编码!这里仅为学习方便。
三、核心概念:3 个关键角色
MyBatis 的工作流程可以用一句话概括:Java 接口 → XML/注解 SQL → 自动映射结果。
1. 实体类(Entity)
就是普通的 Java Bean,和数据库表字段对应。
// src/main/java/com/example/model/User.java
package com.example.model;
public class User {
private int id;
private String name;
private int age;
// 必须有无参构造函数(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 int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "', age=" + age + "}";
}
}
2. Mapper 接口
定义你要执行的数据库操作,不需要实现类!
// src/main/java/com/example/mapper/UserMapper.java
package com.example.mapper;
import com.example.model.User;
import java.util.List;
public interface UserMapper {
User findById(int id);
List<User> findAll();
void insert(User user);
void update(User user);
void delete(int id);
}
3. Mapper XML 文件(或注解)
告诉 MyBatis 每个方法对应的 SQL 是什么。
我们先用 XML 方式(更灵活,适合复杂 SQL):
<!-- src/main/resources/mapper/UserMapper.xml -->
<?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.mapper.UserMapper">
<select id="findById" resultType="com.example.model.User">
SELECT * FROM users WHERE id = #{id}
</select>
<select id="findAll" resultType="com.example.model.User">
SELECT * FROM users
</select>
<insert id="insert" parameterType="com.example.model.User">
INSERT INTO users(name, age) VALUES (#{name}, #{age})
</insert>
<update id="update" parameterType="com.example.model.User">
UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id}
</update>
<delete id="delete" parameterType="int">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>
✅ 关键点:
namespace必须是 Mapper 接口的全限定名id必须和接口方法名一致#{}是占位符,防止 SQL 注入
四、实战:完成一个完整的 CRUD 示例
现在,让我们把所有东西串起来!
第 1 步:编写工具类获取 SqlSession
// src/main/java/com/example/utils/SqlSessionFactoryUtils.java
package com.example.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class SqlSessionFactoryUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
throw new RuntimeException("加载 MyBatis 配置失败", e);
}
}
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}
第 2 步:编写测试代码
// src/test/java/com/example/AppTest.java
package com.example;
import com.example.mapper.UserMapper;
import com.example.model.User;
import com.example.utils.SqlSessionFactoryUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class AppTest {
@Test
public void testCRUD() {
SqlSession session = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
// 1. 插入
User user = new User();
user.setName("张三");
user.setAge(25);
mapper.insert(user);
session.commit(); // 别忘了提交!
System.out.println("插入成功,ID: " + user.getId()); // 注意:此时 ID 还是 0!
// 2. 查询所有
List<User> users = mapper.findAll();
for (User u : users) {
System.out.println(u);
}
// 3. 更新
if (!users.isEmpty()) {
User first = users.get(0);
first.setName("李四");
first.setAge(26);
mapper.update(first);
session.commit();
}
// 4. 删除
// mapper.delete(1);
// session.commit();
session.close();
}
}
❗ 重要提醒:默认情况下,MyBatis 的
insert不会自动回填主键 ID!如果你需要获取自增 ID,得在<insert>标签里加属性:
<insert id="insert" parameterType="com.example.model.User"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO users(name, age) VALUES (#{name}, #{age})
</insert>
加上这行后,user.getId() 就能拿到数据库生成的 ID 了。
五、新手常见问题 & 解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
BindingException: Invalid bound statement |
Mapper XML 的 namespace 或 id 写错 | 检查 namespace 是否等于接口全名,id 是否等于方法名 |
| 插入后 ID 为 0 | 没开启主键回填 | 在 <insert> 加 useGeneratedKeys="true" 和 keyProperty="id" |
| 中文乱码 | 数据库连接没指定编码 | 在 JDBC URL 加 ?useUnicode=true&characterEncoding=utf8 |
| 找不到配置文件 | resources 目录没被识别 | 确保 mybatis-config.xml 在 src/main/resources 下 |
| 事务没生效 | 忘记 session.commit() |
所有写操作后必须调用 commit() |
🧠 我当初学的时候,就因为没加
commit(),调试了整整一晚上,以为 MyBatis 有 bug……其实是自己漏了事务提交!
六、下一步学习建议
恭喜你,已经掌握了 MyBatis 的基础用法!但要真正用好它,建议你继续深入:
- 学习动态 SQL:
<if>,<choose>,<foreach>等标签,让 SQL 更灵活。 - 掌握关联查询:
<resultMap>处理一对一、一对多关系(比如用户-订单)。 - 集成 Spring Boot:实际项目中几乎都用
mybatis-spring-boot-starter。 - 性能优化:了解一级/二级缓存、懒加载等机制。
🔍 避坑指南:不要一上来就学注解方式!XML 更适合团队协作和复杂 SQL。注解适合简单查询。
结语:让数据库操作回归本质
MyBatis 的哲学很简单:SQL 是你写的,对象是你定义的,框架只是桥梁。它不像 Hibernate 那样试图完全隐藏 SQL,而是让你掌控每一条语句——这对理解数据库原理、优化性能非常有帮助。
我写这篇教程,就是希望你能少走弯路。编程不是背语法,而是理解思想。当你下次看到 #{} 时,别只当它是占位符,想想它背后是如何防止 SQL 注入、如何做类型转换的——那才是真正的“算法”思维。
动手敲一遍代码吧!有问题欢迎在评论区留言,我会尽力解答。下期我们聊聊 MyBatis + Spring Boot 整合实战,敬请期待!

评论 0