【零基础入门】MyBatis新手保姆级教程 —— 从安装到上手,一篇就够!
本文面向Java零基础读者,手把手从零搭建MyBatis工程,完成Maven依赖导入、核心配置编写、实体类与Mapper接口开发,实现全套CRUD单元测试实战演示。详解ORM基础概念、MyBatis完整执行流程、`#{}`与`${}`核心差异及参数传递规范,汇总八大高频报错成因与落地解决方案,附赠动态SQL入门案例,梳理知识重难点、整体架构图解与循序渐进的进阶学习路线,全文遵循CSDN规范排版,讲
📌 前言
你是否有过这样的困惑?
学 Java 连接数据库,JDBC 写了一堆重复代码,烦死了?
听说 MyBatis 很好用,但不知道从哪开始?
看了一堆教程,专业术语太多,看不懂?
别慌!这篇文章就是为你准备的。
我会用最通俗的语言,手把手带你从零搭建一个 MyBatis 项目,每一步都有完整代码 + 详细注释,保证你跟着做就能跑起来!
一句话说清 MyBatis 是什么
MyBatis 就是一个帮你「简化数据库操作」的工具。
还记得用 JDBC 操作数据库吗?你需要:
-
加载驱动
-
获取连接
-
创建 Statement
-
写 SQL
-
处理结果集
-
关闭资源
每次都要写一大堆重复代码,累不累?
MyBatis 的作用就是:你只管写 SQL,剩下的脏活累活它帮你干!
MyBatis 的优缺点
|
说明 |
|
|
✅优点 |
SQL 写在配置文件里,和 Java 代码分离,方便维护 |
|
✅ 优点 |
学习成本低,上手快(比 Hibernate 简单多了) |
|
✅ 优点 |
支持动态 SQL,灵活度高 |
|
✅优点 |
国内企业大量使用,面试必问 |
|
❌ 缺点 |
SQL 还是要自己写(但这也算优点,可控性强) |
|
❌ 缺点 |
字段多的时候,映射配置写起来有点多 |
适用场景
-
📦 中小型项目的数据库操作
-
📦 对 SQL 性能要求较高、需要手动优化 SQL 的场景
-
📦 互联网公司后端开发(国内主流选择)
💡 一句话总结:在国内做 Java 后端开发,MyBatis 几乎是必学技能,没有之一。
🛠️ 一、前期准备
工欲善其事,必先利其器。 在开始写代码之前,先把环境准备好。
1.1 你需要准备这些东西
|
工具 |
版本建议 |
说明 |
|
JDK |
1.8 及以上 |
Java 运行环境 |
|
Maven |
3.6 及以上 |
项目构建和依赖管理工具 |
|
MySQL |
5.7 / 8.0 |
数据库 |
|
IntelliJ IDEA |
社区版/旗舰版均可 |
Java 开发工具(推荐) |
1.2 检查 JDK 是否安装
打开命令行(cmd),输入:
java -version
如果看到类似下面的输出,说明 JDK 已安装:
java version "1.8.0_311"
Java(TM) SE Runtime Environment (build 1.8.0_311-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.311-b11, mixed mode)
⚠️ 如果提示"不是内部或外部命令",说明 JDK 没安装或环境变量没配,去 [Oracle 官网](https://www.oracle.com/java/technologies/downloads/) 下载安装即可。
1.3 检查 Maven 是否安装
mvn -version
看到版本信息就 OK:
Apache Maven 3.8.6
Maven home: D:\apache-maven-3.8.6
Java version: 1.8.0_311
💡IDEA 自带 Maven,如果你不想单独安装,直接用 IDEA 内置的也行。
1.4 准备数据库
确保你的 MySQL 已经安装并启动。打开 MySQL 客户端(Navicat、SQLyog、命令行都行),执行以下 SQL,创建我们练习用的数据库和表:
-- 1. 创建数据库
CREATE DATABASE mybatis_demo DEFAULT CHARACTER SET utf8mb4;
-- 2. 使用该数据库
USE mybatis_demo;
-- 3. 创建用户表
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
username VARCHAR(50) NOT NULL COMMENT '用户名',
password VARCHAR(50) NOT NULL COMMENT '密码',
email VARCHAR(100) COMMENT '邮箱',
age INT COMMENT '年龄',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) COMMENT '用户表';
-- 4. 插入一些测试数据
INSERT INTO user (username, password, email, age) VALUES
('张三', '123456', 'zhangsan@qq.com', 25),
('李四', '654321', 'lisi@qq.com', 30),
('王五', 'abcdef', 'wangwu@163.com', 28),
('赵六', '111111', 'zhaoliu@gmail.com', 22);
执行完后,查看一下数据是否插入成功:
SELECT * FROM user;
你应该看到 4 条数据:
+----+----------+----------+-------------------+------+---------------------+
| id | username | password | email | age | create_time |
+----+----------+----------+-------------------+------+---------------------+
| 1 | 张三 | 123456 | zhangsan@qq.com | 25 | 2024-01-01 10:00:00 |
| 2 | 李四 | 654321 | lisi@qq.com | 30 | 2024-01-01 10:00:00 |
| 3 | 王五 | abcdef | wangwu@163.com | 28 | 2024-01-01 10:00:00 |
| 4 | 赵六 | 111111 | zhaoliu@gmail.com | 22 | 2024-01-01 10:00:00 |
+----+----------+----------+-------------------+------+---------------------+
✅ 数据库准备完毕!
1.5 本教程使用的依赖版本
提前告诉你我们会用到哪些依赖,心里有数:
|
依赖 |
版本 |
作用 |
|
mybatis |
3.5.13 |
MyBatis 核心包 |
|
mysql-connector-java |
8.0.33 |
MySQL 驱动,连接数据库用的 |
|
junit |
4.13.2 |
单元测试 |
|
lombok |
1.18.28 |
简化实体类代码(可选) |
🏗️ 二、核心步骤 —— 从零搭建 MyBatis 项目
🎯 接下来是最核心的部分,跟着一步步做,每一步我都会解释为什么这么做。
步骤 1:用 IDEA 创建 Maven 项目
操作流程:
-
打开 IDEA →
File→New→Project -
左侧选择 Maven(不要勾选 Create from archetype)
-
填写项目信息:
-
Name:
mybatis-demo -
GroupId:
com.example -
ArtifactId:
mybatis-demo
-
-
点击 Create
创建完成后,你的项目结构应该长这样:
mybatis-demo
├── pom.xml ← Maven 配置文件
└── src
├── main
│ ├── java ← 放 Java 代码
│ └── resources ← 放配置文件
└── test
└── java ← 放测试代码
步骤 2:在 pom.xml 中引入依赖
打开项目根目录下的 pom.xml,替换为以下完整内容:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>mybatis-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 打包方式为 jar -->
<packaging>jar</packaging>
<!-- 统一管理版本号 -->
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- ==================== 核心依赖 ==================== -->
<!-- 1. MyBatis 核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<!-- 2. MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
<!-- ==================== 辅助依赖 ==================== -->
<!-- 3. JUnit 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- 4. Lombok:自动生成 getter/setter/toString(可选但推荐) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
<!-- 5. SLF4J + Logback 日志(可选,方便看 SQL 执行日志) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
</project>
💡 添加完之后,点击 IDEA 右上角的 Maven 刷新按钮(🔄),让 IDEA 自动下载依赖。如果下载慢,可以配置阿里云镜像(百度搜"Maven 阿里云镜像配置")。
步骤 3:编写 MyBatis 核心配置文件
在 src/main/resources 目录下,新建文件mybatis-config.xml:
💡 这个文件是 MyBatis 的"总指挥",告诉 MyBatis 怎么连数据库、去哪找 SQL 映射文件。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- MyBatis 核心配置文件 -->
<configuration>
<!-- ====== 1. 全局设置 ====== -->
<settings>
<!-- 开启驼峰命名自动映射:数据库字段 create_time → Java 属性 createTime -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启日志,方便调试时看到执行的 SQL -->
<setting name="logImpl" value="SLF4J"/>
</settings>
<!-- ====== 2. 类型别名:简化映射文件中的类名书写 ====== -->
<typeAliases>
<!-- 给 com.example.entity 包下所有类起别名(别名 = 类名,不区分大小写) -->
<package name="com.example.entity"/>
</typeAliases>
<!-- ====== 3. 数据库环境配置 ====== -->
<environments default="development">
<environment id="development">
<!-- 事务管理器:使用 JDBC 原生事务管理 -->
<transactionManager type="JDBC"/>
<!-- 数据源:使用 MyBatis 内置的连接池 -->
<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=Asia/Shanghai&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- ====== 4. 注册映射文件(告诉 MyBatis 去哪找 SQL) ====== -->
<mappers>
<!-- 方式一:逐个注册(适合映射文件少的时候) -->
<mapper resource="mapper/UserMapper.xml"/>
<!-- 方式二:扫描整个包(映射文件多的时候推荐,但需要接口和XML同包同名) -->
<!-- <package name="com.example.mapper"/> -->
</mappers>
</configuration>
关键配置项解释:
|
配置项 |
作用 |
通俗理解 |
|
|
MyBatis 全局行为设置 |
相当于"偏好设置" |
|
|
自动映射下划线到驼峰 |
|
|
|
给类起短名 |
写 SQL 映射时不用写全类名 |
|
|
数据库连接信息 |
告诉程序怎么连数据库 |
|
|
注册 SQL 映射文件 |
告诉程序 SQL 写在哪 |
⚠️特别注意:`username` 和 `password` 要改成你自己 MySQL 的账号密码!
步骤 4:添加日志配置文件(可选但推荐)
在 src/main/resources 目录下,新建文件 logback.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 打印 MyBatis 执行的 SQL 语句(非常有用!) -->
<logger name="com.example.mapper" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
💡 有了这个配置,运行程序时你能在控制台看到 MyBatis 实际执行的 SQL 语句,方便调试。
步骤 5:创建实体类(Entity / POJO)
💡 实体类就是和数据库表一一对应的 Java 类。 数据库表有几个字段,Java 类就有几个属性。
在 src/main/java 下创建包 com.example.entity,然后新建类 User.java:
package com.example.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.util.Date;
/**
* 用户实体类 —— 对应数据库中的 user 表
*
* @Data 注解会自动生成 getter、setter、toString、equals、hashCode 方法
* @NoArgsConstructor 自动生成无参构造方法
* @AllArgsConstructor 自动生成全参构造方法
*
* 如果你没有安装 Lombok 插件,就把这三个注解去掉,手动写 getter/setter/toString
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id; // 用户ID,对应表中的 id 字段
private String username; // 用户名,对应表中的 username 字段
private String password; // 密码,对应表中的 password 字段
private String email; // 邮箱,对应表中的 email 字段
private Integer age; // 年龄,对应表中的 age 字段
private Date createTime; // 创建时间,对应表中的 create_time 字段(驼峰自动映射)
}
⚠️ 不想用 Lombok? 没关系,手动写也行,这里给出不用 Lombok 的版本:
package com.example.entity;
import java.util.Date;
public class User {
private Integer id;
private String username;
private String password;
private String email;
private Integer age;
private Date createTime;
// 无参构造
public User() {}
// 全参构造
public User(Integer id, String username, String password,
String email, Integer age, Date createTime) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.age = age;
this.createTime = createTime;
}
// ===== getter 和 setter =====
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public Date getCreateTime() { return createTime; }
public void setCreateTime(Date createTime) { this.createTime = createTime; }
// toString 方法,方便打印查看
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", age=" + age +
", createTime=" + createTime +
'}';
}
}
步骤 6:创建 Mapper 接口(DAO 层)
💡 Mapper 接口就是定义"你想对数据库做什么操作"的地方。只写方法签名,不写实现——具体的 SQL 写在 XML 映射文件里。
在 src/main/java 下创建包 com.example.mapper,然后新建接口 UserMapper.java:
package com.example.mapper;
import com.example.entity.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 用户 Mapper 接口(相当于 DAO 层)
*
* 这里只定义方法,不写实现
* 具体的 SQL 语句写在对应的 XML 映射文件中(UserMapper.xml)
*/
public interface UserMapper {
// ==================== 查询操作 ====================
/**
* 根据 ID 查询单个用户
* @param id 用户ID
* @return User对象,找不到返回null
*/
User getUserById(Integer id);
/**
* 查询所有用户
* @return 用户列表
*/
List<User> getAllUsers();
/**
* 根据用户名模糊查询
* @param username 用户名关键字
* @return 匹配的用户列表
*/
List<User> getUsersByUsername(String username);
// ==================== 新增操作 ====================
/**
* 新增用户
* @param user 用户对象
* @return 影响的行数(1表示成功)
*/
int insertUser(User user);
// ==================== 修改操作 ====================
/**
* 修改用户信息
* @param user 用户对象(必须包含id)
* @return 影响的行数
*/
int updateUser(User user);
// ==================== 删除操作 ====================
/**
* 根据 ID 删除用户
* @param id 用户ID
* @return 影响的行数
*/
int deleteUserById(Integer id);
// ==================== 多参数查询示例 ====================
/**
* 根据用户名和年龄查询(演示多参数传递)
* @param username 用户名
* @param age 年龄
* @return 匹配的用户列表
*/
List<User> getUserByNameAndAge(@Param("username") String username,
@Param("age") Integer age);
}
💡 为什么只写接口不写实现类? 因为 MyBatis 会根据你写的 XML 映射文件,自动帮你生成实现类(通过动态代理技术)。你不需要关心实现细节,只管定义方法就行。
步骤 7:编写 XML 映射文件(最核心的一步!)
💡 这个 XML 文件是 MyBatis 的灵魂所在! 每个 Mapper 接口都有一个对应的 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"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
namespace:命名空间,必须填写对应 Mapper 接口的全限定类名
作用:把这个 XML 文件和 UserMapper 接口绑定在一起
-->
<mapper namespace="com.example.mapper.UserMapper">
<!--
============================================================
resultMap:结果映射(当字段名和属性名不一致时使用)
因为我们开启了驼峰映射,所以这里其实可以不写
但为了让你了解这个重要功能,我还是写出来作为演示
============================================================
-->
<resultMap id="userResultMap" type="User">
<!-- id 标签映射主键字段 -->
<id column="id" property="id"/>
<!-- result 标签映射普通字段 -->
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="email" property="email"/>
<result column="age" property="age"/>
<result column="create_time" property="createTime"/>
</resultMap>
<!--
============================================================
SQL 片段:把重复的 SQL 提取出来,避免到处复制粘贴
============================================================
-->
<sql id="userColumns">
id, username, password, email, age, create_time
</sql>
<!--
============================================================
1. 查询:根据 ID 查询单个用户
id → 对应接口中的方法名
parameterType → 参数类型(基本类型可以省略)
resultType → 返回值类型(这里用了别名 User,全名是 com.example.entity.User)
============================================================
-->
<select id="getUserById" parameterType="int" resultType="User">
SELECT
<include refid="userColumns"/>
FROM user
WHERE id = #{id}
</select>
<!--
2. 查询:查询所有用户
返回的是 List<User>,但 resultType 只写集合中元素的类型
-->
<select id="getAllUsers" resultType="User">
SELECT
<include refid="userColumns"/>
FROM user
ORDER BY id ASC
</select>
<!--
3. 查询:根据用户名模糊查询
模糊查询用 CONCAT 拼接 %,避免 SQL 注入
-->
<select id="getUsersByUsername" parameterType="string" resultType="User">
SELECT
<include refid="userColumns"/>
FROM user
WHERE username LIKE CONCAT('%', #{username}, '%')
</select>
<!--
4. 新增用户
useGeneratedKeys="true" → 获取数据库自动生成的主键
keyProperty="id" → 把生成的主键值赋给 User 对象的 id 属性
-->
<insert id="insertUser" parameterType="User"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (username, password, email, age)
VALUES (#{username}, #{password}, #{email}, #{age})
</insert>
<!--
5. 修改用户信息
#{xxx} 中的 xxx 对应 User 对象的属性名
-->
<update id="updateUser" parameterType="User">
UPDATE user
SET username = #{username},
password = #{password},
email = #{email},
age = #{age}
WHERE id = #{id}
</update>
<!--
6. 根据 ID 删除用户
-->
<delete id="deleteUserById" parameterType="int">
DELETE FROM user
WHERE id = #{id}
</delete>
<!--
7. 多参数查询示例
当方法有多个参数时,需要用 @Param 注解指定参数名
XML 中用 #{参数名} 获取
-->
<select id="getUserByNameAndAge" resultType="User">
SELECT
<include refid="userColumns"/>
FROM user
WHERE username = #{username}
AND age = #{age}
</select>
</mapper>
到目前为止,你的项目目录结构应该是这样的:
mybatis-demo
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ ├── entity
│ │ │ └── User.java ← 实体类
│ │ └── mapper
│ │ └── UserMapper.java ← Mapper 接口
│ └── resources
│ ├── mybatis-config.xml ← MyBatis 核心配置
│ ├── logback.xml ← 日志配置
│ └── mapper
│ └── UserMapper.xml ← SQL 映射文件
└── test
└── java
└── com
└── example
└── MyBatisTest.java ← 测试类(下一步创建)
步骤 8:编写工具类(获取 SqlSession)
💡 在写测试之前,我们先封装一个工具类,避免每次都写一堆重复的初始化代码。
在 src/main/java 下创建包com.example.util,新建类 MyBatisUtil.java:
package com.example.util;
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 java.io.IOException;
import java.io.InputStream;
/**
* MyBatis 工具类
* 作用:统一创建 SqlSession,避免重复代码
*/
public class MyBatisUtil {
// SqlSessionFactory 全局只需要一个(重量级对象,创建开销大)
private static SqlSessionFactory sqlSessionFactory;
// 静态代码块:类加载时执行一次,初始化 SqlSessionFactory
static {
try {
// 1. 读取 MyBatis 核心配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 2. 根据配置文件创建 SqlSessionFactory(工厂模式,生产 SqlSession)
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3. 关闭流
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取 SqlSession 对象
* SqlSession 是 MyBatis 操作数据库的核心对象,包含执行SQL、提交事务、回滚事务等方法
* @param isAutoCommit 是否自动提交事务
* true:自动提交,执行完SQL自动生效
* false:手动提交,需要调用 sqlSession.commit() 才生效
*/
public static SqlSession getSqlSession(boolean isAutoCommit) {
return sqlSessionFactory.openSession(isAutoCommit);
}
/**
* 重载方法:默认关闭自动提交(推荐,方便控制事务)
*/
public static SqlSession getSqlSession() {
return getSqlSession(false);
}
}
步骤9:编写测试类,验证项目能否运行
激动人心的时刻到了! 我们来测试每一个 CRUD 操作。
在 src/test/java 下创建包 com.example,新建测试类 MyBatisTest.java:
package com.example;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import com.example.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* MyBatis CRUD 测试类
* 每个测试方法可以单独运行(点击方法左边的绿色三角按钮)
*/
public class MyBatisTest {
// ==================== 查询测试 ====================
/**
* 测试1:根据 ID 查询用户
*/
@Test
public void testGetUserById() {
// 1. 获取 SqlSession(相当于一次数据库会话)
SqlSession sqlSession = MyBatisUtil.getSqlSession(true);
try {
// 2. 获取 Mapper 接口的代理对象(MyBatis 自动生成实现类)
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 3. 调用方法,就像调用普通 Java 方法一样简单!
User user = userMapper.getUserById(1);
// 4. 打印结果
System.out.println("========== 查询结果 ==========");
System.out.println(user);
} finally {
// 5. 关闭 SqlSession(释放资源,很重要!)
sqlSession.close();
}
}
/**
* 测试2:查询所有用户
*/
@Test
public void testGetAllUsers() {
SqlSession sqlSession = MyBatisUtil.getSqlSession(true);
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getAllUsers();
System.out.println("========== 所有用户 ==========");
for (User user : userList) {
System.out.println(user);
}
System.out.println("共查询到 " + userList.size() + " 条数据");
} finally {
sqlSession.close();
}
}
/**
* 测试3:根据用户名模糊查询
*/
@Test
public void testGetUsersByUsername() {
SqlSession sqlSession = MyBatisUtil.getSqlSession(true);
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 搜索用户名中包含"三"的用户
List<User> users = userMapper.getUsersByUsername("三");
System.out.println("========== 模糊查询结果 ==========");
for (User user : users) {
System.out.println(user);
}
} finally {
sqlSession.close();
}
}
// ==================== 新增测试 ====================
/**
* 测试4:新增用户
*/
@Test
public void testInsertUser() {
// 注意:增删改操作建议不自动提交,手动控制事务
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 创建新用户对象
User newUser = new User();
newUser.setUsername("孙七");
newUser.setPassword("sun777");
newUser.setEmail("sunqi@qq.com");
newUser.setAge(26);
// 执行插入
int rows = userMapper.insertUser(newUser);
// 手动提交事务(重要!不提交数据不会真正写入数据库)
sqlSession.commit();
System.out.println("========== 新增结果 ==========");
System.out.println("影响行数:" + rows);
System.out.println("新用户的自增ID:" + newUser.getId()); // 自动回填的ID
} catch (Exception e) {
// 出错时回滚事务
sqlSession.rollback();
System.err.println("新增失败:" + e.getMessage());
} finally {
sqlSession.close();
}
}
// ==================== 修改测试 ====================
/**
* 测试5:修改用户信息
*/
@Test
public void testUpdateUser() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 先查出来,再修改
User user = userMapper.getUserById(1);
System.out.println("修改前:" + user);
// 修改信息
user.setEmail("zhangsan_new@qq.com");
user.setAge(26);
// 执行更新
int rows = userMapper.updateUser(user);
sqlSession.commit();
// 再查一次,验证是否修改成功
User updatedUser = userMapper.getUserById(1);
System.out.println("修改后:" + updatedUser);
System.out.println("影响行数:" + rows);
} catch (Exception e) {
sqlSession.rollback();
System.err.println("修改失败:" + e.getMessage());
} finally {
sqlSession.close();
}
}
// ==================== 删除测试 ====================
/**
* 测试6:删除用户
*/
@Test
public void testDeleteUser() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 删除 id=4 的用户(赵六)
int rows = userMapper.deleteUserById(4);
sqlSession.commit();
System.out.println("========== 删除结果 ==========");
System.out.println("影响行数:" + rows);
// 验证:查询所有用户,看赵六是否还在
List<User> allUsers = userMapper.getAllUsers();
System.out.println("删除后剩余用户:");
allUsers.forEach(System.out::println);
} catch (Exception e) {
sqlSession.rollback();
System.err.println("删除失败:" + e.getMessage());
} finally {
sqlSession.close();
}
}
// ==================== 多参数查询测试 ====================
/**
* 测试7:多参数查询
*/
@Test
public void testGetUserByNameAndAge() {
SqlSession sqlSession = MyBatisUtil.getSqlSession(true);
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.getUserByNameAndAge("张三", 25);
System.out.println("========== 多参数查询结果 ==========");
for (User user : users) {
System.out.println(user);
}
} finally {
sqlSession.close();
}
}
}
运行测试
在 IDEA 中打开 MyBatisTest.java:
- 运行单个测试:点击某个测试方法左边的 绿色三角形 ▶️
- 运行所有测试:点击类名左边的 绿色三角形 ▶️
成功运行后,你应该在控制台看到类似这样的输出(以 testGetAllUsers 为例):
✅ MyBatis 初始化成功!
10:30:15.123 [main] DEBUG com.example.mapper.UserMapper.getAllUsers - ==> Preparing: SELECT id, username, password, email, age, create_time FROM user ORDER BY id ASC
10:30:15.156 [main] DEBUG com.example.mapper.UserMapper.getAllUsers - ==> Parameters:
10:30:15.178 [main] DEBUG com.example.mapper.UserMapper.getAllUsers - <== Total: 4
========== 所有用户 ==========
User(id=1, username=张三, password=123456, email=zhangsan@qq.com, age=25, createTime=...)
User(id=2, username=李四, password=654321, email=lisi@qq.com, age=30, createTime=...)
User(id=3, username=王五, password=abcdef, email=wangwu@163.com, age=28, createTime=...)
User(id=4, username=赵六, password=111111, email=zhaoliu@gmail.com, age=22, createTime=...)
共查询到 4 条数据
🎉 恭喜!你的第一个 MyBatis 项目成功运行了!
✅ 三、
三、核心知识点讲解
代码跑起来了,现在让我们回头理解背后的原理,做到知其然更知其所以然。
3.1 什么是 ORM?
ORM(Object Relational Mapping)= 对象关系映射
别被这个名词吓到,其实很简单:
表格
| 数据库中的概念 | Java 中的概念 | 映射关系 |
|---|---|---|
| 一张表(table) | 一个类(class) | user 表 ↔ User 类 |
| 一行数据(row) | 一个对象(object) | 一行用户数据 ↔ 一个 User 对象 |
| 一个字段(column) | 一个属性(field) | username 字段 ↔ username 属性 |
简单说:ORM 就是让 Java 对象和数据库表之间自动 "对号入座"。
MyBatis 属于半自动 ORM 框架(SQL 还要自己写),Hibernate 属于全自动 ORM 框架(连 SQL 都帮你生成)。
3.2 MyBatis 核心执行流程
┌─────────────────────────────────────────────────────────┐
│ MyBatis 执行流程 │
└─────────────────────────────────────────────────────────┘
① 读取配置文件 mybatis-config.xml
│ ↓
② 创建工厂对象 SqlSessionFactoryBuilder
│ .build(inputStream)
│ ↓
③ 得到工厂 SqlSessionFactory
│ ↓
④ 打开会话 SqlSession
│ (一次数据库对话)
│ ↓
⑤ 获取 Mapper 代理 sqlSession.getMapper(UserMapper.class)
│ ↓
⑥ 调用方法 → 执行 SQL userMapper.getUserById(1)
│ ↓
⑦ 返回结果 → 映射为对象 User 对象
│ ↓
⑧ 关闭 SqlSession sqlSession.close()
用大白话解释这个流程:
表格
| 步骤 | 做了什么 | 生活类比 |
|---|---|---|
| ① 读取配置文件 | 告诉 MyBatis 数据库在哪、账号密码多少 | 告诉快递员收件地址 |
| ② 创建工厂 | 根据配置创建一个 "生产线" | 开了一家工厂 |
| ③ 打开会话 | 建立一次数据库连接 | 打了一通电话 |
| ④ 获取 Mapper | 拿到操作数据库的工具 | 拿到了工具箱 |
| ⑤ 调用方法 | 执行具体的 SQL 操作 | 用工具干活 |
| ⑥ 关闭会话 | 释放连接资源 | 挂断电话 |
3.3 映射文件常用标签说明
表格
| 标签 | 用途 | 对应 SQL |
|---|---|---|
<select> |
查询 | SELECT |
<insert> |
新增 | INSERT |
<update> |
修改 | UPDATE |
<delete> |
删除 | DELETE |
<resultMap> |
自定义结果映射 | — |
<sql> |
定义可复用的 SQL 片段 | — |
<include> |
引用 SQL 片段 | — |
<if> |
条件判断(动态 SQL) | — |
<where> |
智能 WHERE 条件(动态 SQL) | — |
<foreach> |
循环遍历(动态 SQL) | — |
3.4 #{} 和 ${} 的区别(面试常考!)
这是 MyBatis 中最容易搞混的两个东西:
表格
| 对比 | #{} | ${} |
|---|---|---|
| 本质 | 预编译参数(PreparedStatement 的?) | 字符串直接拼接 |
| 安全性 | ✅ 防止 SQL 注入 | ❌ 有 SQL 注入风险 |
| 使用场景 | 绝大多数情况都用这个 | 动态表名、动态排序字段 |
| 示例 | WHERE id = #{id} → WHERE id = ? | ORDER BY ${columnName} → ORDER BY age |
🔑 记住一句话:能用 #{} 就用 #{} ,只有在不能用的地方(表名、列名)才用 ${}。
3.5 参数传递方式总结
表格
| 场景 | 接口写法 | XML 中取值 |
|---|---|---|
| 单个基本类型参数 | getUserById(Integer id) | #{id} 或 #{随便写} |
| 单个对象参数 | insertUser(User user) | #{属性名} 如 #{username} |
| 多个参数 | getUser(@Param("name") String name, @Param("age") Integer age) | #{name}, #{age} |
| Map 参数 | getUser(Map<String, Object> params) | #{key 名} |
💡 新手建议:多个参数时一定要加 @Param 注解,否则 MyBatis 不知道哪个参数对应哪个。
四、常见报错及解决办法
新手学 MyBatis 的过程中,踩坑是在所难免的。下面我把最常见的 8 个坑列出来,附上解决办法。
坑 1:找不到配置文件
报错信息:
java.io.IOException: Could not find resource mybatis-config.xml
原因: 配置文件没放在 src/main/resources 根目录下。
解决办法:
- ✅ 确认
mybatis-config.xml放在src/main/resources/下 - ✅ 确认文件名拼写正确(注意大小写)
- ✅ 在 IDEA 中右键 resources 文件夹,确认它被标记为
Resources Root(文件夹图标带黄色横线)
坑 2:Mapper XML 文件没注册
报错信息:
org.apache.ibatis.binding.BindingException:
Type interface com.example.mapper.UserMapper is not known to the MapperRegistry.
原因: 你写了 Mapper 接口和 XML 文件,但忘了在 mybatis-config.xml 中注册。
解决办法: 在 mybatis-config.xml 的 <mappers> 中添加:
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
坑 3:namespace 写错
报错信息:
org.apache.ibatis.binding.BindingException:
Invalid bound statement (not found): com.example.mapper.UserMapper.getUserById
原因: XML 文件中 namespace 和接口的全限定名不一致。
解决办法:检查 XML 中的 namespace:
<!-- ✅ 正确:必须是接口的完整包名+类名 -->
<mapper namespace="com.example.mapper.UserMapper">
<!-- ❌ 错误示例 -->
<mapper namespace="UserMapper">
<mapper namespace="com.example.dao.UserMapper">
坑 4:XML 中的 id 和接口方法名不一致
报错信息:
Invalid bound statement (not found): com.example.mapper.UserMapper.findUserById
原因: XML 中 <select> 标签的 id 必须和接口中的方法名完全一致。
<!-- 接口中方法名是 getUserById -->
<!-- ✅ 正确 -->
<select id="getUserById" ...>
<!-- ❌ 错误 -->
<select id="findUserById" ...>
<select id="get_user_by_id" ...>
坑 5:增删改后数据没变化
现象: 代码没报错,但数据库里数据没有变化。原因: 没有提交事务! 这是新手最容易犯的错。
解决办法(二选一):
运行
// 方式1:手动提交
SqlSession sqlSession = MyBatisUtil.getSqlSession(); // 默认不自动提交
// ...... 执行增删改 ......
sqlSession.commit(); // ⚠️ 别忘了这一行!
// 方式2:开启自动提交
SqlSession sqlSession = MyBatisUtil.getSqlSession(true); // 传入 true
坑 6:数据库连接失败
报错信息:
com.mysql.cj.jdbc.exceptions.CommunicationsException:
Communications link failure
排查清单:
- ✅ MySQL 服务是否启动了?(
net start mysql或在服务管理中查看) - ✅ 数据库地址、端口是否正确?(默认是
localhost:3306) - ✅ 用户名和密码是否正确?
- ✅ 对应数据库是否已创建?
- ✅ MySQL 8.0 的驱动类名是
com.mysql.cj.jdbc.Driver(不是旧版com.mysql.jdbc.Driver)
坑 7:字段和属性映射不上,查询结果为 null
现象: 查询出来的对象,某些属性是 null,但数据库里明明有值。原因: 数据库字段名(如 create_time)和 Java 属性名(如 createTime)不一致。
解决办法(三选一):
<!-- 方法1:开启驼峰映射(推荐!最省事) -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 方法2:在 SQL 中起别名 -->
SELECT create_time AS createTime FROM user
<!-- 方法3:使用 resultMap 手动映射 -->
<resultMap id="userMap" type="User">
<result column="create_time" property="createTime"/>
</resultMap>
坑 8:XML 文件中特殊字符报错
报错信息:
The content of elements must consist of well-formed character data or markup.
原因: SQL 中包含 <、>、& 等 XML 特殊字符。
解决办法:
<!-- ❌ 错误写法:< 在 XML 中是特殊字符 -->
<select id="getUsersByAge" resultType="User">
SELECT * FROM user WHERE age < 30
</select>
<!-- ✅ 方法1:使用转义字符 -->
<select id="getUsersByAge" resultType="User">
SELECT * FROM user WHERE age < 30
</select>
<!-- ✅ 方法2:使用 CDATA 区段(推荐) -->
<select id="getUsersByAge" resultType="User">
SELECT * FROM user WHERE age <![CDATA[ < ]]> 30
</select>
常用转义字符对照表:
表格
| 特殊字符 | 转义写法 |
|---|---|
| < | < |
| > | > |
| & | & |
| " | " |
| ' | ' |
🔥 五、进阶:动态 SQL 入门(附加彩蛋)
什么是动态 SQL?就是根据不同的条件,动态拼接不同的 SQL 语句,是 MyBatis 最强大的特性之一。
在 UserMapper.xml 追加如下代码:
<!--
动态 SQL 示例:条件查询
根据传入的参数动态拼接 WHERE 条件
- 如果传了 username,就按 username 查
- 如果传了 age,就按 age 查
- 都传了,就两个条件一起查
- 都没传,就查所有
-->
<select id="getUserByCondition" parameterType="User" resultType="User">
SELECT
<include refid="userColumns"/>
FROM user
<!-- <where> 标签自动处理第一个条件多余的 AND/OR -->
<where>
<!-- <if> 标签:条件判断,test 写 OGNL 表达式 -->
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="age != null">
AND age = #{age}
</if>
<if test="email != null and email != ''">
AND email = #{email}
</if>
</where>
ORDER BY id ASC
</select>
UserMapper.java 接口新增方法:
/**
* 动态条件查询
*/
List<User> getUserByCondition(User user);
测试代码:
@Test
public void testDynamicQuery() {
SqlSession sqlSession = MyBatisUtil.getSqlSession(true);
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 场景1:只按用户名查
User condition1 = new User();
condition1.setUsername("张");
System.out.println("=== 按用户名模糊查 ===");
userMapper.getUserByCondition(condition1).forEach(System.out::println);
// 场景2:只按年龄查
User condition2 = new User();
condition2.setAge(25);
System.out.println("=== 按年龄查 ===");
userMapper.getUserByCondition(condition2).forEach(System.out::println);
// 场景3:用户名 + 年龄同时查
User condition3 = new User();
condition3.setUsername("张");
condition3.setAge(25);
System.out.println("=== 按用户名+年龄查 ===");
userMapper.getUserByCondition(condition3).forEach(System.out::println);
// 场景4:不传任何条件 → 查所有
System.out.println("=== 查所有 ===");
userMapper.getUserByCondition(new User()).forEach(System.out::println);
} finally {
sqlSession.close();
}
}
💡 同一个方法,传入不同参数生成不同 SQL,这就是动态 SQL 的魅力!
📝 六、总结
学习重点回顾
表格
| 序号 | 知识点 | 掌握程度 |
|---|---|---|
| 1 | MyBatis 是什么、解决什么问题 | ⭐⭐⭐ 必须理解 |
| 2 | 核心配置文件 mybatis-config.xml 的编写 | ⭐⭐⭐ 必须掌握 |
| 3 | 实体类、Mapper 接口、XML 映射文件的关系 | ⭐⭐⭐ 必须掌握 |
| 4 | 基本 CRUD 操作 | ⭐⭐⭐ 必须掌握 |
| 5 | #{} 和 ${} 的区别 | ⭐⭐⭐ 面试必考 |
| 6 | SqlSession 的使用和事务提交 | ⭐⭐⭐ 必须掌握 |
| 7 | resultMap 结果映射 | ⭐⭐ 重要 |
| 8 | 动态 SQL(if、where、foreach) | ⭐⭐ 重要 |
整体架构图解
text
┌─────────────────────────────────────────────────────────────┐
│ 你的 Java 代码 │
│ userMapper.getUserById(1) │
└─────────────────────┬───────────────────────────────────────┘
│ 调用
▼
┌─────────────────────────────────────────────────────────────┐
│ Mapper 接口层 │
│ UserMapper.java(只定义方法,不写实现) │
└─────────────────────┬───────────────────────────────────────┘
│ MyBatis 自动绑定
▼
┌─────────────────────────────────────────────────────────────┐
│ XML 映射文件 │
│ UserMapper.xml(写具体的 SQL 语句) │
│ SELECT * FROM user WHERE id = #{id} │
└─────────────────────┬───────────────────────────────────────┘
│ 执行 SQL
▼
┌─────────────────────────────────────────────────────────────┐
│ MySQL 数据库 │
│ user 表中的数据 │
└─────────────────────────────────────────────────────────────┘
后续进阶学习路线
text
MyBatis 入门(✅ 你在这里)
│
├── 📖 动态 SQL 深入(if、choose、foreach、trim)
│
├── 📖 MyBatis 多表关联查询(一对一、一对多、多对多)
│
├── 📖 MyBatis 缓存机制(一级缓存、二级缓存)
│
├── 📖 MyBatis 注解开发(用注解替代 XML)
│
├── 📖 MyBatis 分页插件(PageHelper)
│
├── 📖 MyBatis 代码生成器(MyBatis Generator)
│
├── 🚀 MyBatis + Spring 整合
│
└── 🚀 MyBatis + Spring Boot 整合(企业级开发必学)
└── MyBatis-Plus(MyBatis 增强工具,更加简化开发)
📌 最后说两句:MyBatis 是 Java 后端开发的基本功,面试必问,工作天天用。这篇教程的代码都是经过验证的,建议你亲手敲一遍(不要复制粘贴),敲完之后再试着自己从零写一遍,印象会深刻很多。
如果这篇文章对你有帮助,点赞👍 + 收藏⭐ + 关注,后续我会继续更新 MyBatis 进阶系列教程!有问题欢迎在评论区留言,我看到都会回复!💬
更多推荐

所有评论(0)