前言
Mybatis 它是一款 Java 的持久层框架. 一说到持久层, 大家肯定想到的就是 MySQL 等数据库, 还有 JDBC 等工具. 哪 mybatis、MySQL、JDBC 之间有什么神秘关系呢 ?
从他们之间的定义上看 :
MySQL 它是一种流行、开源的关系型数据库, 支持多用户并发访问, 高可用性和可扩展性, 并且能够存储海量的数据, 同时 MySQL还可以通过网络协议与客户端进行交互.
JDBC 它是一种基于 Java 语言的数据库访问规范, 提供了一些列与数据库进行交互的 API, 能够让开发人员使用 Java 语言来操作各种关系型数据库
Mybatis 是基于 Java 的持久层框架, 它将 JDBC 操作封装成简单的易用 API, 并且提供了灵活的 SQL 映射配置功能. 通过 Mybatis 可以很好地执行数据库的增、删、改、查功能.
总结起来就是, Mybatis 就是进一步封装了 JDBC 的 API 可以用 Java 去实现操作数据库的持久层框架. 之前咱们用 JDBC 去操作数据库的时候, 是非常麻烦的. 现在用 Mybatis 去操作, 虽然它是基于 JDBC 的, 但是它提供了更高级的和灵活的映射关系, 支持自定义 SQL、存储过程并且免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
先来看看咱们之前是如何操作 JDBC
public static void main(String[] args) throws SQLException {
// 使用 jdbc 往数据中插入一个记录
// 需要提前准备好数据库(库名) -- 0321
// DataSource 相比于 DriverManager 内置了数据库连接池, 可以重复利用连接
// DriverManager 使用的时候需要借助反射, 反射不属于常规编程手段
// 1. 创建数据源, 描述了服务器在哪儿
// 驱动包中提供的具体类
DataSource dataSource = new MysqlDataSource(); // 向上转型 DataSource里包括了MysqlDataSource
((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/java0321?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("password");
// 向下转型 避免后续使用 DataSource 扩散
// 2. 和数据库建立网络连接,(写 jdbc 代码本质上实现一个 mysql 客户端, 通过网络和服务器进行通信
// "连接操作" 才是真正的开始网络通信
// Connection 选择 java.sql 包下的
Connection connection = dataSource.getConnection();
// 通过控制台来输入用户信息(动态 sql 构造)
Scanner scanner = new Scanner(System.in);
System.out.println("请输入学号: ");
int id = scanner.nextInt();
System.out.println("请输入姓名: ");
String name = scanner.next();
// 3. 构建一个 sql 语句, 来完成插入操作
String sql = "insert into student values(?, ?)";
// jdbc 中 还需要搭配一个特定对象来描述这里的 sql 情况
// preparedStatement 来执行具体 sql 语句 会对 sql 语句进行预处理比如解析
PreparedStatement statement = connection.prepareStatement(sql);
// TODO: 占位符 从 1 开始
statement.setInt(1,id); // id 以整数的形式替换第一个占位符
statement.setString(2,name); // name 以字符串的形式替换第二个占位符
System.out.println("sql : " + statement);
// 4. 执行 sql 语句 (控制客户端服务器发送请求)
// 执行操作, 给服务器发送网络请求, 针对 增 删 改 使用 executeUpdate
// 针对 查 使用 executeQuery
int ret = statement.executeUpdate();
System.out.println(ret);
// ret 结果表示 有几行受到此次 sql 语句影响
// 5. 断开和数据库的连接, 并释放必要的资源
// 资源释放顺序和创建顺序相反 先创建 connection 后 创建 statement
statement.close();
connection.close();
}
可以看到, 是非常的复杂繁琐的. 而用 Mybatis 的话, 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO. 当学完 Mybatis 后在来写这个代码对比就知道 Mybatis 的简洁了.
一. 添加 Mybatis 框架支持
创建 Spring Boot 项目时直接添加 mybatis 框架支持的起步依赖
一定要选择数据库客户端具体是哪个, 否则 Mybatis 是不知道操作那个数据库的. 如果你不想创建一个新项目, 也可以通过下面这个方法在当前项目中引入依赖支持
在这里面选择要添加的依赖就可以了
二. 设置 Mybatis 的配置信息
之前咱们创建好 Spring Boot 项目后, 当启动了加载成功后就可以正确启动 Spring Boot 项目了, 但当我们引入 Mybatis 后, 直接去启动是无法启动的
原因很简单, 我们只是添加了 Mybatis 在 Spring Boot 项目中的框架支持, 选择了操作那种类型的数据库 ( 此处选的是 MySQL Driver ), 但是并没有具体的告诉框架, 到底操作谁的数据库, 张三的还是李四的 ? 因此需要先给 Mybatis 设置配置信息
1. 建立数据库连接
在配置文件中, 和指定数据库建立连接, driver-class-name: com.mysql.cj.jdbc.Driver 是根据版本来确定的.
spring:
datasource:
url: jdbc:mysql://localhost:3306/java0525?characterEncoding=utf8&useSSL=false
username: root
password: lyf2021
driver-class-name: com.mysql.cj.jdbc.Driver
2. 配置 XML 路径
MyBatis 的 XML 中保存是查询数据库的具体操作 SQL
mybatis:
mapper-locations:
- classpath:mapper/**Mapper.xml
# - classpath:mybatis/**Mapper.xml // 建议用这个名称
这里的 XML 命名格式需要注意, *Mapper.xml 代表后续如果我要创建某个类的 XML 配置文件, 那么我只能以 XXMapper.xml 后缀命名. 这里的后缀大家是可以自行按照习惯修改的. 建议使用 Mapper 表示映射, 一眼就可以看出来 XXMapper.xml 是谁的映射 XML 文件. 而 mappr 数据持久层是固定在 resources 目录底下的, 这个目录名也是可以自己定义的, 建议改成 mybatis
三. Mybatis 如何执行
按照 Mybatis 的模式是什么 ? 下面通过一幅图来理解
对于 Mybatis 来说只有两个文件, 一个是给别人调用的 Interface 接口, 通过注入的方式就可以直接使用了. 但是奇怪的是, 接口里面是没有具体业务代码实现的, 但是我们使用 Mybatis 是半自动的需要自己来构造 SQL 语句的, 哪 SQL 写到哪 ? SQL 写在 xml 文件里, 也就是说针对每一个 数据库的功能, 都会对应两个组件即 Interface 和 针对 Interface 的实现
但是有一个很可怕的问题, 在文件里怎么实现 Interface 的功能呢 ? 这就是 Mybatis 的神奇之处了, 它就可以在 XML 文件中实现 Interface 里面的方法
实际上, 它是通过代理的方式实现的, 对于用户来说我们感知的是两个东西, 一个是 Interface 接口, 一个是 XMl 文件, 但是到了 Mybatis 的时候, 运行时 Mybatis 会生成一个代理, 然后代理对象把 Interface 里面的方法申明和 XML 里面方法的实现组合成一个代理方法进行填充. 这个时候在其他层( 例如 服务层 )去调用的时候, 调用的就是这个代理对象了, 也就是一个普通类了, 普通类里面自然就有方法和方法的实现. 总的来说就是 Mybatis 框架会替我们封装好这一系列过程.
四. Mybatis 的简单使用
通过 Mybatis 的开发模式知道, 必须有两个文件, 因此在使用 Mybatis 之前需要创建好, 例如下面这个业务
1. 先建立数据持久层 - mapper 层
2. 数据持久层的两个组件之一 ( Interface 接口 )
创建好接口后, 最重要的是必须加上 @Mapper 注解, 因为这不是一个普通的接口, 这是 Mybatis 的接口, 是需要和 Mybatis 中的 xml 文件对应起来的, 而 @Mapper 注解做的就是这个事, 把这个对象托管给框架.
在这个接口里, 我们实现一个查询操作, Mybatis 操作的是数据库, 因此肯定是和数据库有关的, 简单的在 MySQL 中建立一个数据库
-- 创建表用户表
ddrop 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 timestamp default current_timestamp,
updatetime timestamp default current_timestamp,
`state` int default 1
) default charset 'utf8mb4';
建立好以后就是这个效果
查询的是用户, 返回的是用户这个实体类, 因此我们还需要构建用户实体类, 根据数据库中的字段构建.
需要注意, 实体类的字段一定要和数据库中一致, 否则 Mybatis 无法帮我们正确的进行映射从而导致该列为空值
@Data
public class UserEntity {
private int id;
private String username;
private String password;
private String photo;
private LocalDateTime createtime;
private LocalDateTime updatetime;
private int state;
}
接口方法中实现一个查询全部用户操作
@Mapper
public interface userMapper {
// 此处实现一个查询操作
// 接口中默认是 public 方法, 可以不写
List<UserEntity> getAllUser();
}
3. 数据持久层的两个组件之一 ( xml 文件 - interface 接口方法的实现 )
在 mybatis 数据库持久层包底下建立 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="这里面写你 XML 文件要去实现关联的接口类的路径 点你得接口名称">
</mapper>
比如我要实现我建立的 UserMapper 接口, 就是通过 namespace 来进行映射的, 路径不正确是无法正确将接口和 XML 方法实现关联起来的
由于我们此处是查询操作, 因此使用一个查询 SQL 标签, 标签至少设置两个属性, 一个是 id 表明你关联的接口里面要实现的具体是哪个方法的 id, 另一个是 resultType 返回类型, 同样是返回的结果类的 路径 + 类名
最后构造 SQL 就好, 就算完成了
4. 让外部访问
根据标准分层的调用中, Controller 调用 Service, Service 调用 mapper. 从内向外写, 先建立 Service 层
4.1 建立 service 层调用 mapper
建立 service 包, 里面放的是 service 层的类
结合我们上面说的 Mybatis 模式, 此处的 UserMapper 是 Mybatis 的组件之一, 负责提供给外部调用的接口, UserMapper.xml 就是对这个 UserMapper 接口里方法的实现. 在 UserService 里面将 UserMapper 注入, 并且提供服务层调用方法, 在这个方法里面去调用 mapper 层的 UserMapper 接口的方法. 由于 Mybatis 已经将接口和XML 结合填充给代理对象, 因此注入的 UserMapper 是可以当做普通对象使用的( 里面包含方法, 属性等 )
@Service // 托管给 Spring 框架
public class UserService {
// 如果不托管给 Spring, UserMapper 是无法注入进来的
@Autowired
private UserMapper userMapper;
// userMapper 注入进来后, 由于 XML 已经帮其实现了接口里的方法, 因此可以直接调用
public List<UserEntity> getAllUser() {
// mybatis 将接口和XML进行整合成一个代理对象, 此时调用的就是普通类代理对象的方法
return userMapper.getAllUser(); // 调用 mapper 里的查询方法
}
}
4.2 建立 controller 层调用 service 层
建立 controller 包, 里面放的是 controller 层的类
此处 UserService 里面提供的调用方法里面已经去调用了 mapper 层里的查询方法, 因此在 controller 中通过注入 UserService 就可以通过调用 UserService 中的方法而成功调用 mapper 层中的查询方法.
@RestController
@RequestMapping("/user")
public class UserController {
// UserService 已经调用了 mapper 层的接口方法
// 并且提供了供外部调用 UserService 的方法
@Autowired
private UserService userService;
@RequestMapping("getAllUser")
public List<UserEntity> getAllUser() {
return userService.getAllUser(); // 调用 UserService 里的查询方法
}
}
4.3 调用 controller 里的路由方法访问观察
先看看我们的数据库中 userInfo 用户表里有什么
调用路由方法访问看看能查询到这条数据嘛 ? 可以看到没有问题, 非常的 OK
这里采用的是 List 去接受, 有可能是多个用户, 那我们新增一个用户在查询看看, 依然是没有问题的
五. 对 Mybatis 简单应用的图形总结
对于刚刚上面说的 Mybatis 的业务应用, 看起来还是有点不太好理解, 有点方法里面套方法的味道, 下面通过这幅图来帮助大家理解. 上面只是简单的使用了 查询标签,关于 Mybatis 中更多的标签使用, 可以看我后面的文章更新