今天,我用一个完整的用户管理案例,带你从零搭建一个 MyBatis 项目,一步步感受 MyBatis 的魅力!
一、什么是 MyBatis?为什么要用它?
1. MyBatis 是什么?
-
MyBatis 是一款优秀的基于Java的持久层框架内部对JDBC做了封装,使开发者只需要关注SQL语句,而不用关注JDBC的代码,使开发变得更加的简单。
-
MyBatis通过XML或者注解的方式将要执行的各种Statement对象配置起来,通过Java对象和statement中SQL的动态参数进行映射,并最终执行SQL语句。执行SQL后,最终将结果已Java对象返回。
-
采用了ORM的思想(ORM的主要功能是将数据库表映射为类,将表中的记录映射为对象。具体的作用自己百度搜索)。

2. MyBatis 解决了什么痛点?
| 痛点 | MyBatis 的解决方案 |
|---|---|
| JDBC 代码冗余 | 只需要关注 SQL 本身,MyBatis 帮你处理连接、Statement、结果集映射 |
| SQL 硬编码在 Java 代码中 | SQL 写在 XML 文件里,修改 SQL 不用动 Java 代码,维护方便 |
| 手动封装结果集 | 通过 resultType/resultMap 自动把数据库结果映射成 Java 对象 |
| 参数设置麻烦 | 用#{}占位符自动处理参数类型和 SQL 注入问题 |
MyBatis的入门程序(重点)
2.1 创建数据库和表结构
打开cmd命令窗口---输入“mysql -u root -p”---输入密码---进入数据库
--创建数据库
CREATE DATABASE mybatis_db;--使用刚才创建的数据库
USE mybatis_db;--创建名为user的表
CREATE TABLE user(id INT PRIMARY KEY AUTO_INCREMENT,username VARCHAR(32),birthday DATE,sex CHAR(1),address VARCHAR(128)
);--插入数据
INSERT INTO user VALUES(1,"老王","2018-02-27","男","北京");
INSERT INTO user VALUES(2,"熊大","2018-03-02","女","上海");
INSERT INTO user VALUES(3,"熊二","2018-03-04","女","深圳");
INSERT INTO user VALUES(4,"光头强","2018-03-04","男","广州");
二、项目整体结构预览
先看一下我们这个入门项目的完整目录,后面所有的文件都会围绕这个结构展开:
MybatisLearning01
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── qcby
│ │ │ └── mybatis01
│ │ │ ├── mapper # Mapper接口(持久层)
│ │ │ │ └── UserMapper.java
│ │ │ └── model # 实体类
│ │ │ └── User.java
│ │ └── resources
│ │ ├── mappers # SQL映射文件
│ │ │ └── UserMapper.xml
│ │ └── SqlMapConfig.xml # MyBatis主配置文件
│ └── test
│ └── java
│ └── com
│ └── qcby
│ └── mybatis01
│ └── MybatisTest.java # 测试类
└── pom.xml # Maven依赖配置

大致了解一下各个文件的作用:
pom.xml
位置:项目根目录
作用:Maven 项目的核心配置文件,用来管理项目依赖、构建配置。
你的配置里:引入了 MyBatis、MySQL 驱动、JUnit、Log4j 等依赖,是项目运行的 “依赖管家”。
SqlMapConfig.xml
位置:src/main/resources
作用:MyBatis 的主配置文件
负责:配置数据库连接信息(environment 节点)配置事务管理、连接池加载所有的 Mapper XML 文件(mappers 节点)
我们的配置里:已经配置了 MySQL 连接,并且加载了 mappers/UserMapper.xml,是整个 MyBatis 运行的 “总开关”。
User.java
位置:src/main/java/com/qcby/mybatis01/model
作用:数据库表 user 对应的实体类(POJO),用来封装数据库查询 / 操作的数据。
我们的代码里:包含了 id、username、birthday 等字段,以及对应的 Getter/Setter 和 toString() 方法,是数据的 “载体”。
UserMapper.java
位置:src/main/java/com/qcby/mybatis01/mapper
作用:MyBatis 的持久层接口(Mapper 接口),定义操作数据库的方法。
规则:接口方法名必须和 Mapper XML 中 <select>/<insert> 等标签的 id 完全一致,MyBatis 会通过动态代理生成接口的实现类,执行对应的 SQL。
UserMapper.xml
位置:src/main/resources/mappers
作用:Mapper 接口对应的SQL 映射文件,用来写具体的 SQL 语句,和接口方法绑定。我们的代码里:namespace 绑定了 com.qcby.mybatis01.mapper.UserMapper,告诉 MyBatis 这个 XML 对应哪个接口每个 <select>/<insert> 标签的 id 对应接口里的方法名,resultType 定义返回值类型包含了 selectAll、insertUser、findByCount 等 SQL 语句,是 MyBatis 执行数据库操作的 “指令集”。
MybatisTest.java
位置:src/test/java/com/qcby/mybatis01
作用:JUnit 测试类,用来测试 MyBatis 各个方法是否正常运行。
三、MyBatis的入门步骤
3.1 创建 Maven 项目
用 IDEA 创建一个普通的 Maven 项目,不用选任何骨架,直接下一步即可。



3.2 在pom.xml中引入核心依赖
-
引入MyBatis的3.5.13的版本的坐标
-
引入MySQL驱动的jar包,8.0.33版本
-
引入Junit单元测试的jar包,4.9版本
-
引入log4j的jar包,1.2.17版本(需要引入log4j.properties的配置文件)
四个依赖的作用:
-
mysql-connector-java:Java 连接 MySQL 的驱动
-
junit:单元测试,运行测试方法
-
mybatis:MyBatis 框架本身
-
log4j:输出日志,查看执行的 SQL
<?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.qcby</groupId><artifactId>MybatisLearning01</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.9</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.13</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency></dependencies></project>
编写下面代码之前,先按照上面的目录结构创建相应文件夹:
3.3 编写User的实体类,属性尽量使用包装类型,具体的代码如下:
用来接收数据库返回的数据,属性和数据库表的字段一一对应:
package com.qcby.mybatis01.model;import java.util.Date;//接收数据库返回的数据
//参数载体
//实体类//数据库表 user 对应的实体类(POJO),用来封装数据库查询 / 操作的数据。public class User{private Integer id;private String username;private Date birthday;private String sex;private String address;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 Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", birthday=" + birthday +", sex='" + sex + '\'' +", address='" + address + '\'' +'}';}
}
3.4 编写UserMapper的接口和方法(注意:这是个接口)
package com.qcby.mybatis01.mapper;import java.util.List;
import com.qcby.mybatis01.model.User;/*** User业务模块的持久层接口* 调用sql语句操作数据库**/
//MyBatis 的持久层接口(Mapper 接口),定义操作数据库的方法。public interface UserMapper {//查询所有数据的方法public List<User> selectAll();//新增数据的接口方法public int insertUser(User user);public User findById(Integer userId);public void update(User user);public void delete(Integer userId);public List<User> findByName(String username);public Integer findByCount();}
// 接口方法名必须和 UserMapper.XML 中 <select>/<insert> 等标签的 id 完全一致,MyBatis 会通过动态代理生成接口的实现类,执行对应的 SQL。
3.5 在resources目录下,创建mapper文件夹。编写UserMapper.xml的配置文件,导入约束文件。(这是子配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.qcby.mybatis01.mapper.UserMapper"><!--正确的 namespace 必须和接口的包名 + 类名完全一致...告诉 MyBatis:“这个 XML 里的 SQL 都是给 UserMapper 这个接口用的”
没有它,MyBatis 就不知道这个 XML 对应的是哪个接口,接口里的方法也找不到对应的 SQL--><!--每个 <select>/<insert> 标签的 id 对应接口里的方法名,resultType 定义返回值类型--><!--resultType="com.qcby.mybatis01.model.User"--><!--这是给 MyBatis 看的,用来告诉它查询结果要封装成什么类型--><!--它的值必须和你的 User.java 实体类的全类名完全一致,告诉 MyBatis:“这个 selectAll 查出来的结果,每一行数据都要封装成 User 对象”--><!--没有它,MyBatis 就不知道怎么把数据库的结果转换成 Java 对象,查询出来的数据就没法直接用--><select id="selectAll" resultType="com.qcby.mybatis01.model.User">select id, username, birthday, sex, address from user</select><insert id="insertUser" parameterType="com.qcby.mybatis01.model.User">insert into user (username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})</insert><!--通过id查询SQL语句使用#{占位符的名称,名称可以任意},仅限于基本数据类型和String类型--><select id="findById" resultType="com.qcby.mybatis01.model.User" parameterType="int">select * from user where id = #{id};</select><!-- 修改 --><update id="update" parameterType="com.qcby.mybatis01.model.User">update user set username = #{username},birthday = #{birthday},sex = #{sex},address=#{address} where id = #{id}</update><!-- 删除 --><delete id="delete" parameterType="int">delete from user where id = #{id}</delete><!-- 模糊查询 --><select id="findByName" resultType="com.qcby.mybatis01.model.User" parameterType="string"><!-- 第一种方式的SQL语句select * from user where username like #{username}--><!-- 第二章SQL语句的编写 强调:'%${value}%'不能修改,固定写法(不推荐使用) -->select * from user where username like '%${value}%'</select><!-- 具体函数的查询 --><select id="findByCount" resultType="java.lang.Integer">select count(*) from user</select></mapper>
需要注意的是:
-
mapper namespace=“com.qcby.mybatis01.mapper.UserMapper”,叫名称空间,表明以后查找UserMapper接口中的selectAll的方法。
-
select id=“selectAll”中的id属性编写的UserMapper接口中的方法的名称,固定的。
-
resultType=“com.qcby.mybatis01.model.User”表明的是selectAll方法的返回值类型。
3.6 编写主配置文件,在resources目录下创建SqlMapConfig.xml的配置文件(其实名称可以任意),导入对应的约束,编写主配置文件。
这个文件是 MyBatis 的 “总开关”,主要做两件事:
-
配置数据库连接信息
-
加载所有的 SQL 映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--这是主配置文件,主要做两件事--><!--第一件事是配置连接数据库的环境们--><!--第二件事是加载子配置文件--><!--配置连接数据库的环境们--><environments default="mysql"><!--配置具体的环境--><environment id="mysql"><!-- 配置事务管理类型 --><!--JDBC管理事务是关闭自动提交,改成手动提交--><transactionManager type="JDBC"/><!-- 配置是否需要使用连接池,POOLED使用,UNPOOLED不使用 --><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql:///mybatis_db"/><!--最后是数据库的名称--><property name="username" value="root"/><property name="password" value="2020"/></dataSource></environment></environments><!--加载子配置文件:加载映射的配置文件--><mappers><mapper resource="mappers/UserMapper.xml"/></mappers>
</configuration>
注意:
这里的 url、username、password 要改成你自己的数据库信息,MySQL8.0 的驱动要写com.mysql.cj.jdbc.Driver。
3.7 在test文件夹下编写测试类
MyBatis 的执行流程可以总结为:加载主配置文件 → 创建SqlSessionFactory → 获取SqlSession → 获取Mapper代理对象 → 执行SQL → 提交事务(增删改) → 关闭资源
我们来写一个完整的测试类,把所有功能都跑一遍:
package com.qcby.mybatis01;//测试类
//测试单元 测试mybatis框架的入门import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import com.qcby.mybatis01.mapper.UserMapper;
import com.qcby.mybatis01.model.User;
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.apache.ibatis.io.Resources;
import org.junit.Test;import java.io.InputStream;public class MybatisTest {// 测试mybatis的查询所有操作
//JUnit 测试类,用来测试 MyBatis 各个方法是否正常运行。@Testpublic void selectAllRun() throws IOException {//1.加载mybatis的总配置文件 目的:连接数据库 加载子配置文件,找到sql// 加载主配置文件,目的是构建SqlSessionFactory的对象InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");//2.加载完成使用流,构建会话工厂// 创建SqlSessionFactory对象SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);//3.通过会话工厂对象,获取一个会话,执行sql// 使用SqlSessionFactory工厂对象创建SqlSession对象SqlSession session = factory.openSession();// 通过session创建UserMapper接口的代理对象UserMapper mapper = session.getMapper(UserMapper.class);// 调用查询所有的方法List<User> list = mapper.selectAll();// 遍历集合for (User user : list) {System.out.println(user);}// 释放资源session.close();in.close();}@Test//新增数据操作public void run2() throws Exception {//不使用// 加载配置文件InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");// 构建SqlSessionFactory对象SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);// 获取到session对象SqlSession session = factory.openSession();// 查询所有的数据List<User> list = session.selectList("com.qcby.mybatis01.mapper.UserMapper.selectAll");// 变量集合for (User user : list) {System.out.println(user);}// 关闭资源session.close();inputStream.close();}public void insertUserRun() throws IOException {//1.加载mybatis的总配置文件 目的:连接数据库 加载子配置文件,找到sql// 加载主配置文件,目的是构建SqlSessionFactory的对象InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");//2.加载完成使用流,构建会话工厂// 创建SqlSessionFactory对象SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);//3.通过会话工厂对象,获取一个会话,执行sql// 使用SqlSessionFactory工厂对象创建SqlSession对象SqlSession session = factory.openSession();// 通过session创建UserMapper接口的代理对象UserMapper mapper = session.getMapper(UserMapper.class);User user=new User();user.setUsername("hahh");user.setAddress("上海");user.setBirthday(new Date());user.setSex("男");int i=mapper.insertUser(user);//需要手动提交---》Mysql数据库才有这个数据session.commit();System.out.print("新增执行成功影响了"+i+"行数据!!!");// 释放资源session.close();in.close();}@Testpublic void testUpdate() throws Exception {// 1. 加载配置文件InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");// 2. 构建 SqlSessionFactorySqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);// 3. 获取 session(默认手动提交事务)SqlSession session = factory.openSession();// 4. 获取 mapper 代理对象UserMapper mapper = session.getMapper(UserMapper.class);// 业务逻辑:修改用户User user = mapper.findById(41);user.setUsername("小风");mapper.update(user);// 提交事务(增删改必须提交!)session.commit();// 关闭资源session.close();inputStream.close();}@Testpublic void testDelete() throws Exception {// 1. 加载配置文件InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");// 2. 构建 SqlSessionFactorySqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);// 3. 获取 sessionSqlSession session = factory.openSession();// 4. 获取 mapperUserMapper mapper = session.getMapper(UserMapper.class);// 业务逻辑:删除用户mapper.delete(48);// 提交事务session.commit();// 关闭资源session.close();inputStream.close();}@Testpublic void testFindByName() throws Exception {// 1. 加载配置文件InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");// 2. 构建 SqlSessionFactorySqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);// 3. 获取 sessionSqlSession session = factory.openSession();// 4. 获取 mapperUserMapper mapper = session.getMapper(UserMapper.class);// 业务逻辑:模糊查询List<User> list = mapper.findByName("%王%");for (User user : list) {System.out.println(user);}// 查询操作不需要提交事务,但还是要关闭资源session.close();inputStream.close();}// 第二种
// @Test
// public void testFindByName() throws Exception {
// List<User> list = mapper.findByName("王");
// for (User user : list) {
// System.out.println(user);
// }
// }@Testpublic void testFindByCount() throws Exception {// 1. 加载配置文件InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");// 2. 构建 SqlSessionFactorySqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);// 3. 获取 sessionSqlSession session = factory.openSession();// 4. 获取 mapper 代理对象UserMapper mapper = session.getMapper(UserMapper.class);// 业务逻辑:查询总记录数Integer count = mapper.findByCount();System.out.println("总记录数:"+count);// 关闭资源session.close();inputStream.close();}
}
四、关键知识点总结
4.1 #{} 和 ${} 的区别
-
#{}:预编译 SQL,相当于 JDBC 的?占位符,自动处理参数类型,可以防止 SQL 注入,推荐使用。
-
${}:直接拼接 SQL,不会预编译,有 SQL 注入风险,只有在动态表名、排序字段等特殊场景才会用到。
4.2 增删改必须提交事务
MyBatis 的openSession()默认是手动提交事务,所以执行insert/update/delete后,必须调用session.commit(),否则数据不会真正写入数据库。
4.3 接口绑定的规则
-
Mapper 接口的全类名必须和 XML 文件的namespace完全一致。
-
接口的方法名必须和 XML 中
<select>/<insert>等标签的id完全一致。 -
方法的返回值类型必须和 XML 中resultType一致(或兼容)。
五、Idea中MybatisX插件的下载
在IDEA项目中
settings--Plugins---MybatisX插件,这里我没这个插件,需要下载
MybatisX插件的主要功能是:Mapper 接口与 XML 双向跳转
-
在 UserMapper.java 的方法上,会出现一个小鸟图标,点击直接跳转到 XML 里对应的 SQL 标签
-
在 UserMapper.xml 的
<select>标签上,也能反向跳转到对应的接口方法 -
再也不用手动在两个文件里来回找了,解决了 “接口和 SQL 对不上” 的问题。

-
先点击蓝色链接 Search in repositories
-
进入仓库后,在搜索框里输入 MybatisX,就能找到对应的插件,点击安装即可。

安装完成之后关闭窗口即可

然后apply---ok---restart,即可

我们就能看到UserMapper接口与子配置文件UserMapper.xml的来回跳转

