目录
1. 什么是 MyBatis
2. 如何学习 MyBatis
2.1 搭建 MyBatis 开发环境
2.2 使用 MyBatis 模式和语法操作数据库
2.2.1 MyBatis 的组成
2.2.2 MyBatis 实现查询功能
2.2.3 MyBatis 实现新增功能
2.2.3 MyBatis 实现删除功能
2.2.3 MyBatis 实现修改功能
3. 在 MyBatis 的基础上实现程序的完整交互
1. 什么是 MyBatis
MyBatis 是一款神级持久层框架, 它支持自定义 SQL , 存储过程以及高级映射. 简单来说, MyBatis 就是基于 JDBC 做到更简单的完成程序与数据库交互的高级框架. -- MyBatis 官网
MyBatis 也是一个 ORM 框架, ORM (Object Relational Mapping), 即对象关系映射, 在面向对象编程语言中,将关系型数据库中的数据与对象建立起映射关系,进而自动的完成数据与对象的互相转换. (JDBC 更像是面向过程的编程思想, MyBatis 更像是面向对象的编程思想)
2. 如何学习 MyBatis
学习 MyBatis 可以分为两个大的方向学习:
-
搭建 MyBatis 开发环境
-
使用 MyBatis 模式和语法操作数据库
2.1 搭建 MyBatis 开发环境
第一步: 创建一个 Spring Boot 项目, 并添加 MyBatis 框架支持
此时你的程序是不能正常运行的.
第二步: 配置数据库的连接信息
在 resource 目录下创建一个 applicaiton.yml 文件 (根据自己喜好, properties 和 yml 都行)
【注意】如果使用的 mysql-connector-java 是 5.x 系列及以前的就可以使用我上面的驱动, 大于 5.x 系列的就使用 "com.mysql.cj.jdbc.Driver".
第三步: 配置 MyBatis 的 XML 文件存放位置和命名规则
yml 配置文件中的 MyBatis/**Mapper.xml 的意思是 :
- 在 resource 下创建一个 MyBatis 目录, 然后所有的 xml 都存放在这个目录下, xml 里面写的就是 SQL 语句. 当然 MyBatis 是我自己取的名字, 这个可以随意命名.
- **Mapper.xml 就表示在 Mybatis 目录下创建的 xml 文件的命名要以 Mapper.xml 结尾, 例如 UserMapper.xml
配置 mapper-location 的作用是需要搭配一个注解来说的, 后面会提到.
此时我们的项目就可以正常运行起来了.
2.2 使用 MyBatis 模式和语法操作数据库
2.2.1 MyBatis 的组成
- 接口 【当前类的所有方法声明 (CRUD 方法)】
- xxxMapper.xml 【CRUD 的 SQL语句】
MyBatis 由接口和 xml 组成, 一个类会有一个接口和一个对应实现相应操作的 xml 文件.
- 接口中是增、删、改、查等方法的声明, 存在的目的就是给别人调用的.
- xml 中存放的就是增删改查的 SQL 语句, xml 中的每条 SQL 和接口中的方法一一对应. (sql 如果存写在类中, 需要用双引号引起来, 有时候有变量还需要处理拼接, 太麻烦, 所以存放在 xml 中)
大致流程图:
2.2.2 MyBatis 实现查询功能
先准备数据 - 建库建表
-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;
-- 使用数据数据
use mycnblog;
-- 创建表[用户表]
drop table if exists userinfo;
create table userinfo(
id int primary key auto_increment,
username varchar(100) not null,
password varchar(32) not null,
photo varchar(500) default '',
createtime datetime default now(),
updatetime datetime default now(),
`state` int default 1
) default charset 'utf8mb4';
-- 准备一条数据
insert into userinfo (username,password,photo)
values ('李白','123','cat.jpg');
第一步: 创建实体类
@Data
public class UserInfo {
private int id;
private String username;
private String password;
private String photo;
private Date createtime;
private Date updatetime;
private int state;
}
- 实体类是用于 MyBatis 做数据库表的映射的.
- 要保证实体类中的属性名和数据库表中的字段名一致.
第二步: 创建一个接口
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper; // MyBatis 以前叫做 ibatis
import java.util.List;
@Mapper // 此注解必不可少, 该注解会将 接口 和 xml 对应起来
public interface UserMapper {
// 查询
public List<UserInfo> selectAll();
}
【重点】接口上必须要添加 @Mapper 注解, 此注解的作用:
- 将当前接口实例化 (后续其它类中需要使用该接口, 只需要注入就行了)
- 关联接口中的方法和对应 xml 的具体实现 (第三步有详细讲到)
第三步: 创建与接口对应的 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.demo.mapper.UserMapper">
</mapper>
>>> MyBatis 的接口和 xml 是如何关联起来的 ? 【重点理解】
1. xml 的创建和命名规则前面在 yml 中已经配置了 (根据自己的配置) , 必须在创建在 MyBatis 文件夹下, 命名要以 Mapper.xml 结尾. 当程序运行的时候, MyBatis 会去找它自己的注解 @Mapper, 然后再根据 yml 文件中配置的路由再去匹配当前接口所有对应的 xml 文件中的具体实现.从而将接口和 xml 关联起来. 这一切的一切都是 MyBatis 去做的, 但是我们必须要遵守规范!!
2. 上面 MyBatis 中的 xml 模板是通用的, 唯独要自己写的地方就是 namespace, namespace 里需要配置当前 xml 要实现的接口的路径, 配置好 xml 时, 此时接口和 xml 就关联上了.
该路径是: 包名 + 接口名. 如下图:
第四步: 在 xml 中写 SQL 语句
<?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.demo.mapper.UserMapper">
<select id="selectAll" resultType="com.example.demo.model.UserInfo">
select * from userinfo
</select>
</mapper>
1. 查询使用 <select> 标签
- id 属性上的 selectAll 对应接口中的方法名 【千万不敢写错】
- resultType 表示查询返回对象的类型 (包名 + 实体类类名) 【千万不敢漏写】
2. <select> 标签写 SQL 语句, 最后的分号可以省略.【建议在 cmd 上执行成功后再复制进来】
>>> SQL 语句是如何将查询结果带回的呢 ? 【重点理解】
我们的查询语句首先会从数据库中查到数据, 然后再根据 resultType 去找对应的实体类, 然后通过 setXXX() 方法, 将查到的每条数据的每个字段设置到实体类的每个属性中, 此时数据库中每条记录的值就能够在对应实体类中的属性上体现出来了, 所以 MyBatis 做的事情就是封装了 JDBC 的代码, 然后将数据保存到 List<UserInfo> 里面了, 此时我们在项目中只要实现注入了, 就能够在任何地方拿到查询的结果了. 所以我们一定要保证实体类属性名和数据库中字段名保持一致!!
(如果不一致, 也有解决方法, 下一篇博客会详细讲到).
第五步: 将接口中对应的方法生成 Spring Boot 单元测试
【单元测试】
单元测试的好处: 以最小成本, 花最短时间去验证我们的程序是否正确.
如何使用单元测试验证程序功能:
- 这一步是不需要我们操作的, 因为我们在创建 Spring Boot 项目的时候, 默认会给每个项目都内置单元测试的框架.
- 在接口中右击选择 Generate -> Test, 然后选中要单元测试的方法, 然后点击 ok, 就会生成如下单元测试类:
这时候, 我们还需要做一些事情, 才能测试查询功能是否正确:
1. 对单元测试的类加上 @SpringBootTest 注解, 表示当前测试的上下文环境为 Spring Boot.
2. 注入需要测试的接口, 这里需要测试 UserMapper (属性注入, Setter 注入, 构造方法注入)
3. 在测试方法中使用接口中的方法, 去实现业务逻辑.
4. 点击测试方法左边的三角形, 运行程序, 验证测试结果.
测试结果:
对比数据库中的记录, 判断查询结果是否与数据库相匹配:
发现结果正确, 查询功能实现完成!!!
2.2.3 MyBatis 实现新增功能
此处我们实现新增并得到用户的自增 id 功能, 这个功能实现了, 那么普通的新增功能就更加没问题了, 所以此处就不单独写新增了.
由于我们现在操作的实体类还是 UserInfo, 所以准备工作在前面已经做好了, 现在只需要做三步:
- 在 UserMapper 接口中声明新增方法.
- 在对应的 xml 中写具体的实现 (写 sql).
- 对新增功能进行单元测试.
第一步: 在接口中声明新增方法
public int insert(@Param("userInfo") UserInfo userInfo);
1. 此处的 @Param 注解可加可不加, 加上更加规范.(个人见解)
2. 前面在学习 Spring MVC 的时候, 接受各种参数都已经掌握的差不多了, 所以此处传递对象就不再过多的赘述了. -- Spring MVC 核心
第二步: 在对应的 xml 中写具体的实现
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into userinfo(username,password,photo)
values(
#{userinfo.username},
#{userinfo.password},
#{userinfo.photo}
)
</insert>
对于普通的增 <insert> 删 <delete> 查 <select> 改 <update> , 只有 <select> 标签除了要设置 id 参数, 还要设置 resultType 或 resultMap 参数, 其他三种标签, 只需要设置 id 参数即可, 但是此处属于特殊的新增, 所以需要多设置 useGeneratedKeys 参数和 keyProperty 参数.
- useGenerateKeys: 设置后会让 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键, 默认值:false.
- keyProperty: 此处设置的就是数据库中的主键在实体类中对应的属性名.
>>> 当接口中的方法来调用 xml 中的具体实现时, 在 xml 中通过 #{} 的方式获取接口中的参数.
【#{} 注意事项】:
1. 如果传递的是普通参数, 只需将参数名写进 #{} 即可.
2. 如果传递的是对象, 则需要使用 #{对象.属性} 的方式去获取.
3. 如果 @Param 注解中的参数名和接口方法中用来接收参数的变量名不同时, 则 #{} 中的参数需要根据 @param 注解中的参数名相匹配.
如果不使用 @Param 注解, 那么以上注意事项第一条还是一样的, 就不存在第 3 条这样的区别了, 但是第 2 条区别还是挺大的.
对于上面第二条注意事项, 我们看到了加 @Param 注解和不加 @Param 注解, xml 中写法上的区别, 但是又有一个共同点: #{} 中设置的参数都是实体类对应的属性名, 所以此处我们在实体类中设置的属性名和数据库中的字段名不相同都没有关系. 由此得出结论 >>>
- 实现查询功能的时候, 属性名必须要和字段名相匹配. 因为数据是从数据库中查出来再将字段一一设置到属性上. (此时属性名和数据库中的字段相关)
- 实现增删改的时候, 属性名不需要和字段名相匹配. 因为增删改是从前端获取到数据再设置到数据库上的字段上. (此时属性名是和前端相关)
但是我们回到 MyBatis ORM (对象关系映射) 框架的初衷上来说, 它实现的功能就是将数据库和程序中的类进行映射, 并且在进行映射的时候就是使用程序中的对象名(属性)和数据库表中的字段名做映射的. 所以一般情况下, 我们应该遵守约定将类中的属性名和表中的字段名全部一一对应上. (当然也有属性名和字段名有时候不相同的特殊情况, 下一篇博客会详细讲解到)
回到我们的新增功能上, 此时我们已经完成两个大的步骤了, 现在只需要生成单元测试, 进行验证我们的新增功能.
第三步: 对新增功能进行单元测试
生成单元测试的步骤还是和查询一样, 就不赘述了. 看代码:
此处 result 接受的是受影响的行数, 获取自增 id , 直接可以从属性中获取.
>>> 为什么我在构造数据的时候没有设置 id, 还能直接调用该对象的 get 方法去拿自增 id 呢?
因为我们在 xml 中设置了开启自增, 并且把返回的结果设置给属性 id, 所以我们在单元测试的时候, 没有设置 id, 调用 getXXX() 方法, 依旧能得到.
执行结果:
查看数据库中是否添加了新的记录:
发现数据成功新增到数据库中, 并且单元测试也拿到了自增 id.
2.2.3 MyBatis 实现删除功能
经过查询和新增功能的学习, 那么删除和修改就如鱼得水了.
删除操作三部曲:
- 在 UserMapper 接口中声明新增方法.
- 在对应的 xml 中写具体的实现 (写 sql).
- 对删除功能进行单元测试.
第一步: 在 UserMapper 接口中声明删除方法.
public int deleteById(@Param("id") Integer id);
第二步: 在对应的 xml 中写具体的实现
<delete id="deleteById">
delete from userinfo where id=#{id}
</delete>
第三步: 对删除功能进行单元测试
执行结果:
查询数据库中的记录:
发现 id 为 2 的用户已经删除, 删除功能成功实现.
2.2.3 MyBatis 实现修改功能
修改操作三部曲:
- 在 UserMapper 接口中声明新增方法.
- 在对应的 xml 中写具体的实现 (写 sql).
- 对修改功能进行单元测试.
第一步: 在 UserMapper 接口中声明修改方法.
public int update(@Param("id")Integer id,
@Param("password") String password);
第二步: 在对应的 xml 中写具体的实现
<update id="update">
update userinfo set password=#{password} where id=#{id}
</update>
第三步: 对修改功能进行单元测试.
执行结果:
查询数据库记录:
发现记录更新成功, 修改操作成功实现.
3. 在 MyBatis 的基础上实现程序的完整交互
我们上面所讲述的都是持久层操作数据库的操作, 还没有提供相应的入口, 算不上一个完整的项目, 一个最小的项目的完整目录都至少分为三层: controller -> service -> Mapper.
接下来, 针对查询功能实现一个简单的交互. Mapper 层有了, 现在只需要添加 Controller 层和 Service 层了.
- 第一步: 添加 Service 层
- 第二步: 添加 Controller 层
- 第三步: 构造 HTTP 请求
第一步: 添加 Service 层
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<UserInfo> selectAll() {
return userMapper.selectAll();
}
}
第二步: 添加 Controller 层
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/select")
public List<UserInfo> selectAll() {
return userService.selectAll();
}
}
第三步: 构造 HTTP 请求
此处为了视觉效果, 先在数据库中多添加几条数据.
浏览器输入 URL : http://127.0.0.1:8080/user/select
发现执行结果和数据库是完全相同的, 到这一步, 我们就已经实现一次完整的交互了.
本篇博客就到这里, 下一篇博客将讲解 MyBatis 中更加进阶一点的知识, 期待大家的关注!!