目录
MyBatis是什么?
MyBatis 是一个持久层框架,它是用 Java 编程语言编写的,用于管理数据库访问代码,帮助开发者在 Java 应用程序中轻松地与数据库进行交互。
MyBatis 的核心思想是将 SQL 语句从 Java 代码中分离出来,使得 SQL 语句和 Java 代码可以分开维护和管理。通过 XML 文件或 注解 来配置 SQL 映射关系,使得开发者可以更灵活地编写和管理 SQL 查询语句。
web应用程序一般分为三层:Controller、Service 和 Dao
浏览器发起请求,先请求Controller,Controller接收到请求后,调用Service进行业务逻辑处理,Service再调用Dao从数据库中获取数据

Controller(表现层):负责接收来自用户的请求并调用相应的业务逻辑进行处理
Service(业务层):负责实现应用程序的业务逻辑,包括对业务规则的处理、数据的验证和计算等
Dao(持久层):负责将应用程序的数据持久化到数据库中,并提供对数据库的访问操作
MyBatis 是持久层框架,其目的是为了更加简单的完成程序和数据库交互,是一个更简单的操作和读取数据库的工具
简单了解了什么是MyBatis后,接下来,我们就来学习如何使用MyBatis
MyBatis入门
要使用MyBatis,我们首先要引入MyBatis相关依赖,配置MyBatis(数据库连接信息)
因此,我们从创建工程开始:
创建工程
创建 SpringBoot 工程,并引入MySQL的驱动包 和 MyBatis的起步依赖:

MyBatis是持久层框架,是帮助我们更简单地操作和读取数据库的工具,其具体数据存储和数据操作还是在MySQL中进行的,因此,需要添加MySQL驱动
项目创建完成后,会在pom.xml中导入MyBatis依赖 和 MySQL驱动依赖:

其中 MyBatis 依赖包的版本会随着SpringBoot版本发生变化:

可参考:mybatis-spring-boot-autoconfigure – Introduction
创建好项目后,我们需要创建数据表及实体类,为数据库操作做好准备
数据准备
创建用户表 userinfo:
-- 若数据库存在,则删除
DROP DATABASE IF EXISTS mybatis_test;
-- 创建数据库
CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;
-- 使用数据库
USE mybatis_test;
-- 创建用户表
DROP TABLE IF EXISTS userinfo;
CREATE TABLE `userinfo` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`username` VARCHAR ( 127 ) NOT NULL,
`password` VARCHAR ( 127 ) NOT NULL,
`age` TINYINT ( 4 ) NOT NULL,
`gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1:男 2:女 0:默认',
`phone` VARCHAR ( 15 ) DEFAULT NULL,
`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0:正常, 1:删除',
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now(),
PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
添加信息:
-- 添加信息
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '12312312311' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 19, 1, '12312312312' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 20, 2, '12312312313' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'zhaoliu', 'zhaoliu', 21, 2, '12312312314' );
此时,就已创建好了用户表:

接下来,我们创建对应的实体类 UserInfo:
import lombok.Data;
import java.util.Date;
@Data
public class UserInfo {
private Integer id;
private String username;
private String password;
private Integer age;
private Integer gender;
private String phone;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
接下来,我们将 MyBatis 连接到数据库
连接数据库
MyBatis中要连接数据库,需要数据库相关参数配置:
MySQL驱动类
登录名
密码
数据库连接字符串
.properties 配置内容:
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_test?
characterEncoding=utf8&useSSL=false
#连接数据库的⽤⼾名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=123123
.yml 配置内容:
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
username: root
password: 123123
driver-class-name: com.mysql.cj.jdbc.Driver
注:若使用的MySQL 是 5.X 之前的,使用 "com.mysql.jdbc.Driver",若 大于 5.X 使用的是 "com.mysql.cj.jdbc.Driver"
接下来,我们来编写持久层代码
编写代码
我们首先创建持久层接口 UserInfoMapper

MyBatis的持久层接口规范一般叫 xxxMapper
接下来,我们进行简单的查询:
import com.example.mybatis.model.UserInfo;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface UserInfoMapper {
@Select("select id, username, password, age, gender phone from userinfo")
public List<UserInfo> selectAllUser();
}
@Mapper注解:表示是MyBatis中的Mapper接口
程序运行时,框架会自动生成接口的实现类对象(代理对象),并给交Spring的IOC容器管理 @Select注解:代表的就是select查询,也就是注解对应方法的具体实现内容
写好代码之后,我们对代码进行测试
单元测试
我们可以在 test 目录下进行测试:

import com.example.mybatis.mapper.UserInfoMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class MybatisApplicationTests {
@Autowired
private UserInfoMapper userInfoMapper;
@Test
void contextLoads() {
System.out.println(userInfoMapper.selectAllUser());
}
}
MybatisApplicationTests添加了注解 @SpringBootTest,因此该测试类在运行时,会自动加载Spring的运行环境
我们只需要通过 @Autowired 注入我们想要测试的类,就可以进行测试了
运行代码,进行测试:

从结果中可以看到,SQL语句中查询的列对应的数据被赋值
此外,我们也可以使用 Idea 自动生成测试类:
我们只想要在需要测试的Mapper 接口中,鼠标右键,选择 Generate

再选择 Test

勾选我们想要测试的方法

点击 OK后,就能够在 Test 目录下生成对应的测试类:

同样的,我们进行测试:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import static org.junit.jupiter.api.Assertions.*;
class UserInfoMapperTest {
@Autowired
private UserInfoMapper userInfoMapper;
@Test
void selectAllUser() {
System.out.println(userInfoMapper.selectAllUser());
}
}
运行代码:
然而此时程序报错:

报错信息显示:userInfoMapper 为空
这说明 userInfoMapper 未成功创建,这是因为我们未在测试类上添加注解 @SpringBootTest,因此,该测试类在运行时不会自动加载Spring运行环境,我们想要通过 @Autowired 注解注入我们要测试的类,自然会失败
我们在测试类上加上 @SpringBootTest,再次运行:

此时结果正确
在使用MyBatis的过程中,我们可以借助日志,来观察 SQL语句的执行、传递的参数 和 执行结果,因此,我们来学习MyBatis日志打印
日志
我们只需要在配置文件中进行配置:
.properties
#指定mybatis输出⽇志的位置(控制台)
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
.yml
mybatis:
configuration: # 配置打印 MyBatis⽇志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
此时我们再次运行程序,就可以看到SQL的执行内容 传递的参数 和 执行结果

在上述操作中,我们并未传递参数,若我们想要查询 id = 2 的用户信息,此时应该如何编写代码呢?
参数传递
查询 id = 2 的用户信息:
@Select("select id, username, password, age, gender, phone from userinfo where id = 2")
public UserInfo selectById();
进行测试:
@Test
void selectById() {
System.out.println(userInfoMapper.selectById());
}
测试结果:

但此时 id 为固定的数值2,因此只能查询 id = 2 的数据,若我们想要变为动态的数值,应该如何实现呢?
我们可以在 selectById方法中添加参数 id,将方法中的参数传给SQL语句
@Select("select id, username, password, age, gender, phone from userinfo where id = #{id}")
public UserInfo selectById(Integer id);
使用 #{} 获取方法中的参数
测试:
@Test
void selectById() {
System.out.println(userInfoMapper.selectById(3));
}
结果:

此时,就将方法中的参数传给SQL语句进行查询
也可以通过 @Param ,设置参数的别名
@Select("select id, username, password, age, gender, phone from userinfo where id = #{userId}")
public UserInfo selectById(@Param("userId") Integer id);
#{}中的属性名必须与 @Param 设置的相同
在学习了MyBatis的使用后,我们来进一步学习 MyBatis 的 增 删 改 查 操作
MyBatis的增删改查
增加(Insert)
若我们使用 SQL语句进行增加:
insert into userinfo (suername, password, age, gender, phone) values ("aaa", "aaa", 22, 2, "135234522")
我们使用 @Insert 注解来实现增加
此时,我们只需要将要添加的数据替换为动态的参数
@Insert("insert into userinfo (username, password, age, gender, phone) values (#{username}, #{password}, #{age}, #{gender}, #{phone})")
public Integer insert(UserInfo userInfo);
在这里,我们可以直接使用 UserInfo对象的属性名来获取参数
接下来,我们进行测试:
@Test
void insert() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("aaa");
userInfo.setPassword("aaa");
userInfo.setAge(22);
userInfo.setGender(1);
userInfo.setPhone("135234522");
System.out.println(userInfoMapper.insert(userInfo));
}
运行结果:

此时新增了一条数据
Insert语句的默认返回值是 受影响的行数
但有时,我们在插入数据后,需要获取到新插入数据的id
若想要拿到自增 id,需要在方法上添加一个Options注解:
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into userinfo (username, password, age, gender, phone) values (#{username}, #{password}, #{age}, #{gender}, #{phone})")
public Integer insert(UserInfo userInfo);
useGeneratedKeys:支持自动生成记录主键的数据库(如 MySQL、SQL Server) ,若将其设置为 true,则在添加记录后可以获取到数据库自动生成的主键id(其默认值为false)
keyProperty:指定能够唯⼀识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)
再次进行测试:
@Test
void insert() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("ccc");
userInfo.setPassword("ccc");
userInfo.setAge(24);
userInfo.setGender(1);
userInfo.setPhone("135234522");
Integer count = userInfoMapper.insert(userInfo);
System.out.println("新增数据条数:" + count + "新增数据id:" + userInfo.getId());
}
设置 useGeneratedKeys = true 后,方法返回值仍为 受影响的行数,自增 id 设置在 keyProperty 指定的属性中

删除(Delete)
若我们使用 SQL语句进行删除:
delete from userinfo while id = 4
我们使用 @Delete 注解来实现增加
此时,我们只需要将要添加的数据替换为动态的参数
@Delete("delete from userinfo where id = #{id}")
public Integer deleteById(Integer id);
同样的,我们进行测试:
@Test
void deleteById() {
System.out.println(userInfoMapper.deleteById(4));
}
测试结果:

修改(Update)
使用 SQL语句进行修改:
update userinfo set username = "admin" where id = 2
我们使用 @Update 注解来实现修改
@Update("update userinfo set username = #{username} where id = #{id}")
public Integer update(UserInfo userInfo);
进行测试:
@Test
void update() {
UserInfo userInfo = new UserInfo();
userInfo.setId(2);
userInfo.setUsername("admin");
System.out.println(userInfoMapper.update(userInfo));
}

查询(Select)
我们查询 id = 2 的数据是否更新成功:
@Select("select * from userinfo where id = #{id}")
public UserInfo selectById(Integer id);
测试:
@Test
void selectById() {
System.out.println(userInfoMapper.selectById(2));
}
结果:

更新成功
但根据结果我们可以发现:其中的 deleteFlag、createTime 和 updateTime 都为 null,未被赋值

而在SQL语句中 查询了 delete_flag、create_time 和 update_time,但这几个属性没有赋值
即 Java对象属性 与 数据库字段 相同的行被赋值
当自动映射查询结果时,MyBatis 会获取结果中返回的列名并在 Java类 中查找 相同名字的属性(忽略大小写)。这意味着如果发现了 ID 列和 id 属性,MyBatis会将列ID的值赋给id属性
因此,当返回的列名未在Java类中找到相同名字的属性,则不会赋值
如何解决这个问题呢?
1. 起别名
我们可以想到我们在学习MySQL时,可以给列名起别名,因此,我们可以通过起别名的方式,让别名 和 实体类属性名相同
@Select("select id, username, password, age, gender, phone, " +
"delete_flag as deleteFlag, create_time as createTime, update_time as updateTime " +
"from userinfo")
public List<UserInfo> selectAllUser();
进行测试:
@Test
void selectAllUser() {
System.out.println(userInfoMapper.selectAllUser());
}
测试结果:

当使用起别名的方式时,每当 实体属性名 与 数据库字段名不同时,都需要起别名,比较麻烦
除了起别名,我们还可以使用 结果映射
2. 结果映射
@Select("select * from userinfo where id = #{id}")
@Results({
@Result(column = "delete_flag", property = "deleteFlag"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
public UserInfo selectById(Integer id);
测试:
@Test
void selectById() {
System.out.println(userInfoMapper.selectById(2));
}
运行结果:

其他SQL也可以复用这个映射关系,但需要为这个Results定义一个名称:
@Select("select * from userinfo")
@ResultMap(value = "resultMap")
public List<UserInfo> selectAllUser();
@Select("select * from userinfo where id = #{id}")
@Results(id = "resultMap", value = {
@Result(column = "delete_flag", property = "deleteFlag"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
public UserInfo selectById(Integer id);
使用 id 属性为 Results 定义别名,使用 @ResultMap 注解来复用其他定义的ResultMap
测试 selectAllUser() 方法:

此外,我们还可以开启驼峰命名
3. 开启驼峰命名
一般数据库字段使用 蛇形命名法(单词之间使用下划线分割)进行命名 ,而Java属性使用驼峰命名法进行命名
因此,为了在这两种命名方式之间启用自动映射,我们可以配置驼峰自动转换、
.properties
mybatis.configuration.map-underscore-to-camel-case=true #配置驼峰⾃动转换
.yml
mybatis:
configuration:
map-underscore-to-camel-case: true #配置驼峰⾃动转换
此时就能够将表中字段名:xxx_xxx 转换为 类中属性名 xxxXXX
此时,我们再次进行测试:
@Select("select * from userinfo where id = #{id}")
public UserInfo selectById(Integer id);
测试结果

字段正确赋值
在上述操作中,我们使用注解的方式来进行增删改查,在MyBatis中,也可以使用XML的方式来进行操作
接下来,我们就来学习使用XML
XML配置文件
要想使用 XML,首先我们需要配置数据库连接字符串和MyBatis
.properties
# 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有表的 xml 文件
mybatis.mapper-locations=classpath:mapper/**Mapper.xml
.yml
mybatis:
# 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有表的 xml 文件
mapper-locations: classpath:mapper/**Mapper.xml
配置好后,开始编写代码
此时的代码分为两部分:
1. 方法定义(Interface)
2. 方法实现(XXX.xml)
我们首先添加 mapper接口:
import java.util.List;
@Mapper
public interface UserInfoXMLMapper {
List<UserInfo> selectAllUser();
}
接下来添加 UserInfoXMLMapper.xml
UserInfoXMLMapper.xml的路径参考配置的路径:

其中 MyBatis的固定 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.mybatis.mapper.UserInfoXMLMapper">
</mapper>
其中<mapper>标签中需要指定 namespace 属性,表示命名空间,值为 mapper 接口的全限定名
接下来,我们使用 <select>查询标签来进行查询操作:
<?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.mybatis.mapper.UserInfoXMLMapper">
<select id="selectAllUser" resultType="com.example.mybatis.model.UserInfo">
select username, password, age, gender, phone from userinfo
</select>
</mapper>
<select> 中的 id 表示对接口的具体实现方法,值为 mapper接口中的定义的方法名
resultType 表示返回的数据类型,也就是我们定义的实体类
接下来,我们进行测试:
同样的,我们生成测试类:

进行单元测试:
@SpringBootTest
class UserInfoXMLMapperTest {
@Autowired
private UserInfoXMLMapper userInfoXMLMapper;
@Test
void selectAllUser() {
System.out.println(userInfoXMLMapper.selectAllUser());
}
}
测试结果:

接下来,我们学习通过xml的方式实现 增删改查
增加(Insert)
UserInfoXMLMapper接口:
Integer insert(UserInfo userInfo);
UserInofXMLMapper.xml实现:
<insert id="insert">
insert into userinfo(username, password, age, gender) values (#{username}, #{password}, #{age}, #{gender})
</insert>
同样的,使用 #{} 获取方法中的参数,使用 @Param设置参数名称
进行测试:
@Test
void insert() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("hhh");
userInfo.setPassword("hhh");
userInfo.setAge(13);
userInfo.setGender(2);
System.out.println(userInfoXMLMapper.insert(userInfo));
}
测试结果:

返回自增 id
同样的,我们设置 useGeneratedKeys 和 keyProperty属性
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into userinfo(username, password, age, gender) values (#{username}, #{password}, #{age}, #{gender})
</insert>
再次进行测试:

删除(Delete)
UserInfoXMLMapper接口:
Integer delete(Integer id);
UserInofXMLMapper.xml实现:
<delete id="delete">
delete from userinfo where id = #{id}
</delete>
测试:

修改(Update)
UserInfoXMLMapper接口:
Integer update(UserInfo userInfo);
UserInofXMLMapper.xml实现:
<update id="update">
update userinfo set age = #{age} where id = #{id}
</update>
测试:

修改成功

查询(Select)
同样的,使用xml的方式查询,也存在有属性没有赋值的问题
<select id="selectAllUser" resultType="com.example.mybatis.model.UserInfo">
select * from userinfo
</select>

其解决方法与注解相似:
1. 起别名
2. 结果映射
3. 开启驼峰命名
其中 起别名 和 开启驼峰命名 与注解相同,在这里就不再演示了,我们重点学习 xml 中如何实现结果映射
<resultMap id="BaseMap" type="com.example.mybatis.model.UserInfo">
<id column="id" property="id"></id>
<result column="delete_flag" property="deleteFlag"></result>
<result column="create_time" property="createTime"></result>
<result column="update_time" property="updateTime"></result>
</resultMap>
其中,
id:resultMap的别名
type:要映射的实体类
id:主键
result:普通字段和属性
column:数据库字段名
property:Java对象属性名
&spm=1001.2101.3001.5002&articleId=138190729&d=1&t=3&u=f406e335f5c343469e494618c0277c29)
4206

被折叠的 条评论
为什么被折叠?



