引言
ORM 框架
ORM(Object Relational Mapping):对象关系映射。
MyBatis 是一个 ORM 框架。在面向对象编程语言中,将关系型数据库中的数据与对象建立起映射关系,进而自动地完成数据与对象的互相转换。
MyBatis 核心
MyBatis 的核心有两个:一是接口,二是 " xml 文件 ".
此外,我认为还有一个较为核心的东西就是实体类。
映射关系:
① 数据表 => 实体类
② 一条记录 => 一个对象
③ 表中字段 => 对象的属性
MyBatis 的第一次使用
一、创建一个 Spring MVC 项目
创建一个 Spring MVC 项目,不再多说。
但当我们真正用到 MyBatis 的时候,就需要选择下面的依赖:
一个是 " MyBatis Framework ",另一个是 " MySQL Driver "
前者表示框架,后者表示驱动 ( 根据你需要连接的数据库商家进行选择 )
二、搭好配置文件
概览步骤
步骤:
步骤1:创建好配置文件 ( yml / properties )
步骤2:选择运行环境 ( 开发环境 / 运行环境 ),【下面步骤以开发环境为例】
步骤3:在配置文件中,连接数据库
步骤4:在配置文件中,开启 MyBatis SQL 打印,以便我们日常测试
步骤5:在配置文件中,配置 " xml 文件 " 保存路径
步骤1
创建 " application.yml "
步骤2
创建 " application-dev.yml " 和 " application-prod.yml "
步骤3
连接数据库固定代码:
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8&&useSSL=false username: root password: 12345678 driver-class-name: com.mysql.cj.jdbc.Driver # 这里表示 MySQL 驱动名称
注意事项:
① 使用 MyBatis 连接数据库是写在配置文件中的,它是系统类别的配置文件,所有的格式都是固定写法。
② 以往我们使用 JDBC 编程的时候, " url ", " username ", " password " 这三个属性是必不可少的,现在呢,我们需要额外添加一个 " driver-class-name " 属性,它表示需要连接哪种数据库的名称。如果你需要连接的是 Oracle 数据库,就填写 Oracle 的专属名称;如果你需要连接的是 MySQL数据库,就填写 MySQL 的专属名称。
步骤4
开启 MyBatis SQL 打印日志
logging: level: com: example: demo: debug mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
注意事项:
这一步不是必须的,但能够方便于我们测试,我认为初学者必须要配置。
步骤5
配置 " xml 文件 " 路径:
mybatis: mapper-locations: classpath:mybatis/**Mapper.xml
注意事项:
① 这里的配置代码,应该写在公共的 " application.yml " 文件下,因为不论是开发环境,还是生产环境,都需要这段公共代码。
② 前文提到,MyBatis 的核心就在于接口和 " xml 文件 " ,所以 " xml 文件 " 就应该放置在 【resources】目录下。此外,由于日后我们的 " xml 文件 " 在一个项目中不止一份,所以我们应该创建一个【mybatis】目录。
③ 这里配置 " xml 文件 " 是一种标准写法,它告知了 MyBatis 框架 " xml 文件 " 是在哪个目录底下的哪种格式,如下图所示:
总览配置
由于我们暂时不涉及云服务器上的数据库,所以我们只展示 " 公共的 yml 文件 " 和 " 开发环境的 yml 文件 ".
注意事项
① MyBatis 框架的配置既多又杂,很多写法都是固定的,这就像 " 1+1=2 " 一样,我们无需知道为什么,直接用就可以了,我们可以将这些零零碎碎的代码片段,放置我们的代码仓库中,方便我们后续使用。
② 从 0 到 1 是比较难的,尤其是这种搭配环境的事情,确实比较折腾人,但多做几次,就能够很好的熟悉整个过程。
三、正式查询数据库
概览步骤
步骤1:准备数据库
步骤2:根据数据库的字段,创建实体类
步骤3:创建 Mapper 接口
步骤4:创建 " xml 文件 "
步骤5:创建 Service 类
步骤6:创建 Controller 类
步骤1
准备数据库:
创建一个名为 " mycnblog " 的数据库,并插入两张表,【用户表】、【文章表】、
我们的思想是博客系统,一个用户拥有自己的专属的文章,并将用户的信息放在用户表中,文章信息放在文章表中。后续,我们可以通过这些字段来增删改查。
-- 创建数据库 drop database if exists mycnblog; create database mycnblog DEFAULT CHARACTER SET utf8; 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 '123.png', createtime datetime default now(), updatetime datetime default now(), `state` int default 1 ); -- 创建【文章表】 drop table if exists articleinfo; create table articleinfo( id int primary key auto_increment, title varchar(100) not null, content text not null, createtime datetime default now(), updatetime datetime default now(), uid int not null, rcount int not null default 1, `state` int default 1 ); -- 添加一个用户信息 insert into userinfo (id, username, password, `state`) values (null, 'Jack', '123', 1); insert into userinfo (id, username, password, photo, `state`) values (null, '李明', '321', '李明.png', 1); insert into userinfo (id, username, password, `state`) values (null, 'Rose', '456', 1); -- 文章添加测试数据 insert into articleinfo(title,content,uid) values ('C++','C++语法',1); insert into articleinfo(title,content,uid) values ('Java','JavaWeb',2); insert into articleinfo(title,content,uid) values ('Python','基本结构',3);
步骤2
根据数据库的字段,创建实体类:
@Data public class UserInfo { private int id; private String username; private String password; private String photo; private String createtime; private String updatetime; private int state; }
注意事项:
创建实体类的步骤非常关键! 我们需要仔细地将 Java 和 数据库对应起来。
① Java 实体类的类名应该和数据表的表名吻合。
② 实体类中的属性 (成员变量) 应该和表中的字段吻合。
为什么一定要这样做?
① 首先,这是一种标准。
② 其次,这里的实体类需要与项目中的 " xml " 文件、mapper 接口联系起来,只要其中一个环节出错,那么就会出现整个项目抛异常的情况,到那个时候,可能就不仅仅是代码层面的报错,也有可能是框架报错。所以,为了避免调试这样的麻烦事,我们不能随心所欲,我们应该与之前数据库设计好的那样去约定实体类。
步骤3
创建 Mapper 接口:
@Mapper public interface UserMapper { // 根据用户 id 来查询某个用户的所有信息 public UserInfo getUserById(@Param("id") Integer id); }
步骤4
创建 " 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"> <select id="getUserById" resultType="com.example.demo.model.UserInfo"> select * from userinfo where id = #{id} </select> </mapper>
注意事项:
① " namespace " 属性,填入的是 Mapper 接口。( 需要具体到包名、接口名 )
② 如果我们进行查询操作,就使用 select 标签;修改操作,就使用 update 标签…
③ 在上面的 select 标签中," id " 属性表示 Mapper 接口中的方法名;" resultType" 属性表示当前查询操作返回的类型 ( 需要具体到包名、实体类名 )
④ 最重要的是 SQL 语句,它决定了我们是否真正对数据库执行了操作。下面的 #{id} 就表示 JDBC 中的问号,表示占位符。后面我还会介绍 ${id}.
select * from userinfo where id = #{id} -- select * from userinfo where id = ?
备注: 在写上面的 SQL 语句之前,我们应该先在自己的数据库上进行原 SQL 的测试,如果原 SQL 没问题了,那么我们再变换形式写在 " xml " 文件中,这样不容易出错。
接口和 " xml 文件 " 的对应关系
接口和 " xml 文件 " 的对应关系是整个 MyBatis 的核心,虽然这里的关联关系并不难,但操作起来非常的乱,且容易出错,所以我们应该重视起来。
步骤5
创建 Service 类:
在 UserService 类中,我们采取 " 属性注入 " 的方式,将 " userMapper " 对象存入进来,之后就能使用接口中的方法了。
@Service public class UserService { @Resource private UserMapper userMapper; public UserInfo getUserById(Integer id) { return userMapper.getUserById(id); } }
备注: 属性注入不仅可以存入类,也可以存入接口。
步骤6
创建 Controller 类:
在 UserController 类中,我们依然采取 " 属性注入 " 的方式,将 " userService " 对象存入进来,之后就能使用类中的方法了。
在这里,我们明确了给前端返回的是一个数据,也明确了路由、传入参数。
@RestController public class UserController { @Resource private UserService userService; @RequestMapping("/get_user") public UserInfo getUserId(Integer id) { if (id == null) return null; return userService.getUserById(id); } }
注意事项
这里需要思考,我们为什么不使用 " Controller 类 " 直接调用 " Mapper 接口 ",而是让 " Service " 在中间插了一杠子?
① 首先,这是一个 Web 项目查询数据库的标准步骤。
② 其次,这样分层地拆分程序,能够更好地解耦合,也能够更好地为后端分工。
与前端交互的事情,就让 " Controller " 层去做,交互出了问题,直接看这一层。
调用接口的事情,就让 " Service " 层去做,出了问题,直接定位这一层。
查询数据库的事情,就让 " Mapper " 接口去做…
四、查询结果验证
后端运行启动类,前端传入一个 id = 2 的参数,查询结果:
我们查看 MyBatis 日志文件:发现 #{id} 就是一个占位符,相当于一个问号,只有前端传入参数进来的时候,才会进行替换。
Spring Boot 单元测试
定义
单元测试,就是指对软件中的最小可测试单元进行检查和验证的过程。
最小可测单元就是方法,方法无法再拆分了。
框架
一般来说,当我们成功创建一个 Spring Boot 项目的时候," pom.xml " 会自动为我们生成下面的依赖。
作用
① 单元测试可以非常简单、直观、快速地测试某一个功能是否正确。
② 使用单元测试可以帮我们在打包的时候,发现一些问题,因为在打包之前,所有的单元测试必须通过,否则就不能打包成功。
③ 使用单元测试,在测试功能的时候,可以不污染连接的数据库,也就是可以不对数据库进行任何改变的情况下,进行测试。( 使用 " @Transactional " 注解实现 )
生成测试类步骤
步骤1: 在你需要进行测试的 类 / 接口 中,点击鼠标右键进行如下操作。
注意事项: 点击鼠标右键之前,我们鼠标必须要挪动到下面的红色框框中,否则就不会出现 " Test " 选项。
步骤2: 勾选你需要测试的成员方法,其他的选项默认即可,框架会自动帮我们生成的。
步骤3: 系统会自动帮我们生成一个测试类,在测试类中,有对应的同名测试方法。测试类的保存路径在 " 绿色的 java 文件下 " ,与 " 蓝色的 java 文件 " 保存形式很相似。
点击方法启动,开始测试
@SpringBootTest // 表示当前单元测试运行在 Spring Boot 环境中 class UserMapperTest { @Resource private UserMapper userMapper; @Test void getUserById() { UserInfo userInfo = userMapper.getUserById(2); System.out.println("测试结果:" + userInfo); } }
测试结果:
注意事项:
① 这是 Spring Boot 的单元测试,所以需要添加 " @SpringBootTest " 注解。
② 使用 " 属性注入 " 的方式来存入对象,后续就可以拿到原类的方法了。
③ 属性注入时,既可以使用 " @Autowired " 注解,也可以使用 " @Resource " 注解。但前者放在专业版 IDEA 下时,软件会报错,但不影响测试运行。
总结
第一次使用 MyBatis 框架的时候,其实是比较难的。我认为,这个难点就是存在于折腾环境这件事上,并不是业务代码层面上的难点。关于配置环境,只要出现一点问题,就会出现意想不到的报错异常,看到你头皮发麻,所以,每次我们配置环境的时候,都需要按照标准写法来,不要随心所欲。只有环境搭建好了,才能真正地使用 SQL 语句。
然而,MyBatis 对 JDBC 进行了封装,只要我们有 JDBC 的基础,学习起来就很快了。最起码,思想层面我们就不用问更多为什么了。