JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔


文章目录

  • 前言
  • 1.MyBatis 是什么?
  • 为什么要学习 MyBatis
  • 怎么学 MyBatis?
  • 1、创建 MyBatis 项目
    • 准备工作:创建数据库 和 数据表
    • 1.1、添加 MyBatis 相关依赖
      • 1、新建一个 MyBatis 项目
      • 2、在老项目中引入 MyBatis 相关依赖
    • 1.2、配置数据库连接字符串
    • 1.3、配置 MyBatis 保存的 xml 的目录
  • 使用 MyBatis 的操作模式 操作数据库
    • MyBatis 的操作模式
    • 第?个MyBatis查询:实现一个根据用户id来查询用户信息的操作
      • 1、定义接口
      • 2、创建 xml,实现上面的接口
      • 运行结果展示
      • 注意!有的人可能会在这一步出现问题
  • MyBatis 执行过程
  • Spring Boot的单元测试 - 穿插内容
    • 1.什么是单元测试?
  • 2、单元测试有哪些好处?
  • 3.Spring Boot 单元测试使用
    • 单元测试的实现步骤
      • 1.?成单元测试类
      • 2 添加单元测试代码
        • 1.添加 Spring Boot 框架测试注解:@SpringBootTest
        • 2.添加单元测试业务逻辑
      • 3、开启测试
      • 4、简单的断?说明
  • 拓展:“小鸟” - 插件 MyBatisX
  • 增、删、该操作
    • MyBatis 修改操作
  • 在不修改数据库中数据的情况下,完成单元测试 - 穿插
    • MyBatis 删除操作
      • 1、在 mapper 中的 UserInfoMapper 接口中定义 方法
      • 2、在xml文件中 编写 SQL语句 的相关代码。
      • 单元测试 - 验证功能效果
    • MyBatis 插入操作
      • 先实现最简单 的 插入操作
        • 1、在 mapper 中的 UserInfoMapper 接口中定义 方法
        • 2、在xml文件中 编写 SQL语句 的相关代码。
        • 3、进行单元测试
      • 上面最简单的实现方式,下面我们来看难一点的实现。
        • 先来看第二步:在xml文件中 编写 SQL语句 的相关代码。
        • 再来看一步:在 mapper 中的 UserInfoMapper 接口中定义 方法。
        • 我们测试一下。
  • 查询操作
    • 单表查询
    • 参数占位符 #{} 和 ${} 的区别
    • ${} 的经典问题:SQL注入问题
    • like 查询 - 特殊情况
    • #{} 和 ${} 的区别总结
    • 多表查询
      • 前置知识
        • 返回类型:resultType
        • 返回字典映射: resultMap
          • resultType && resultMap 的区别
    • 有了 resultMap 和 resultType 的基础,我们下面就可以真正开始进行多表查询的操作了。
      • MyBatis 多表查询:一对一关系
      • MyBatis 多表查询:一对多关系
  • 复杂情况:动态SQL使?
    • 动态标签 - < if >标签
    • 动态标签 - < trim >标签
    • 动态标签 - < where >标签
    • 动态标签 - < set >标签
    • 动态标签 - < foreach >标签

前言
经过前?的学习咱们 Spring 系列的基本操作已经实现的差不多了,接下来,咱们来学习更重要的知识,将前端传递的数据存储起来,或者查询数据库??的数据。
?
1.MyBatis 是什么?
MyBatis 是?款优秀的持久层框架,它?持?定义 SQL、存储过程以及?级映射。
MyBatis 去除了?乎所有的 JDBC 代码以及设置参数和获取结果集的?作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接?和 Java POJO(Plain Old Java Objects,普通?式 Java 对象)为数据库中的记录。
简单来说 MyBatis 是更简单完成程序和数据库交互的?具,也就是更简单的操作和读取数据库?具。
Mybatis官?
?
针对 MyBatis 是?款优秀的持久层框架 进行分析和补充。

MyBatis,也是一个 ORM 框架。
ORM(Object Relational Mapping),即对象关系映射。
在?向对象编程语?中,将关系型数据库中的数据与对象建?起映射关系,进??动的完成数据与对象的互相转换:
1、 将输?数据(即传?对象)+SQL 映射成原? SQL
2、将结果集映射为返回对象,即输出对象
?
ORM 把数据库映射为对象:
数据库表(table)>>> 类(class)
记录(record,?数据)>>> 对象(object)
字段(field) >>> 对象的属性(attribute)
?般的 ORM 框架,会将数据库模型的每张表都映射为?个 Java 类。
也就是说使? MyBatis 可以像操作对象?样来操作数据库中的表,可以实现对象和数据库表之间的转换。
?
即:MyBatis 可以称为是一座“桥梁”。
它的作用就是:将 数据库 和 程序,映射起来。
映射起来之后,就可以做一系列的操作了。
比如:
查询,由于我们将 数据库 与 程序映射起来了,直接就把整个数据表映射到一个类里。
查询的结果,就是一个集合,存储的是类的对象(插叙到的一行行记录),那数据也就查询到了。
?
保存,还是一样的,我们已经将 数据库 与 程序 映射起来了,把表里面的数据,搞到对象里面了。那我们调用对象的某个方法,是不是就可以直接对象的成员直接保存到 MySQL 里面了呢!

MySQL 和 MyBatis,是不一样的。
MySQL,提供了一个 数据存取(数据管理) 的软件。
而 MyBatis 是一个“中间桥梁”,用于连接 程序 和 数据库,建立关系映射的,进行 数据操作 的中间层(持久层)。
总的来说:
MyBatis 的定义:MyBatis 是一个优秀的 ORM(对象关系映射)持久层框架。
其作用:实现 程序 和 数据库 之间的数据交互。

PS:
ORM 框架,并不是只有 MyBatis 一家!
只是说 MyBatis 在国内使用的标胶广泛,大概占据中国市场的90%以上。
国外用的最多的是Hibernate。
?
至于其中的原因,MyBatis 和 Hibernate 之间的区别等等。。。
包括 在 Hibernate 之后,又有一个 Spring JPA,与其又有和区别?
等 MyBatis 讲完之后,再给你们讲。
(前提是我还记得,没写你们就自己百度吧)

MyBatis ?持?定义 SQL
自定义SQL,包含的范围就很广了。
数据库中的 “CURD(增删查改)”语句,都是可以实现的。
自定义SQL嘛,我们想写什么SQL,就写什么SQL。
由此,不难看出 MyBatis 一个 最大的特点:灵活!
这也是为什么 MyBatis 在国内能够流行的重要原因!!!
?
其背后的原因,就是国内用户的需求实在是太奇葩了,参差不齐!!
?
举个生活周围的例子:
几乎所有大学,都会有 计算机专业。
但是!有哪个学校是有 关于 互联网产品的专业?没有!!!
大学不会教,外面培训班又少。
大部分都是 考研的培训班,再其次就是关于 计算机 的培训。
计算机的发展,还是比较成熟的。
但是呢!关于 产品经理 和 产品 的 培训,基本上做这个培训的机构。
反正我是没见过。
原因,就是因为它的市场占有率不高,看不到利益的苗头。
自然也就不会与人去做这件事。
这么说吧:在有些公司,根本就没有产品经理,这个职位。
?
因此,我们以后所遇到的产品经理,大部分都是只长了一张嘴,会提需要,但是不知道这个需求实现起来,有多复杂!
因为没有培训过,再加上现在的产品经理,大部分都是不懂技术的,
因此,他们提出的需求,千奇百怪。
于是就有了 手机壳的那个段子,完全就是扯犊子。
一个软件,一个硬件,八竿子打不着的两个东西。
?
因此,MyBatis 灵活性 的作用,就体现出来了。
就是为了 适应 各种需求。

国外,对于 技术 和 产品 已经流程化了,都是经过培训的。
毕竟计算机,外国才是起源地嘛,发展的快,也是很容易理解的。
发展倒极致,就是稳定了。
存储过程
我之前将的 SQL,不管是多玛复杂的SQL,我们都是一行SQL解决。
因为 SQL中,并没有特别复杂的业务,像 for循环,if判断。。。这些都没有。
就是一个 很平滑,单一维度 的 SQL 操作,这是针对于 普通 SQL。
?
但是!存储过程,是SQL中的方法,它是由一大堆 SQL 的组成的。
这个组成里面,它是有循环的,判断的,分支的,有变量传递的。
这个就叫做存储过程,也就是将 SQL 方法化。
反过来理解:
SQL 方法化 的过程所产生的东西,就叫做存储的过程 。
?
以后,我们工作了之后,别人给我们一存储过程,里面就是密密麻麻的SQL语句。
还有 if 判断,for循环,设置变量的值…
然后,入参,出参,反正就是很大一串的SQL语句。
少则 几十行,多则 上千行。
?
那为什么我们程序员不会这个存储过程呢?
这其实和 存储过程 的 特性相关的。
虽然 存储过程 能够实现复杂的业务,但是!这只是 将业务 从 程序方 移动到了 SQL 方。
这种情况下,公司对 程序员的要求 就会非常高!!!
?
试想一下:公司给一个 存储过程,一个几百行的 SQL语句,你估计眼睛都花了。
你能看得懂,才有鬼。。。
?

但是 总得有人来做吧,这就诞生一个特殊的职业 DBA。
DBA:Database Administrator —— 数据库管理员,又称数据库开发工程师 。
他的SQL是很牛的,他不用写程序的。
每天都是在捣鼓 SQL 语句的,只玩数据库的,玩数据的。
?
对于 大部分程序员来说:根本就搞不懂 存储过程 ,这么复杂的东西!
而且,存储过程中有一个特别特别难用的东西!
我们在敲一个很复杂的 java 代码的时候,可以通过 debug 可以一步一步去调试,进入类,方法,属性中,来观察运行时的状态。从而能够帮助我们排查程序中存在的错误。也及时 程序 是能够调试的。
?
但是 存储过程 是不能调式的。
也就是说:一个存储过程,有这几百行的SQL语句,要我们去写。
而且,出现了错误,只能通过肉眼去排查错误。【不支持调试】
我敢说:百分之90的程序员,都做不到这一点。
这是属于 真正的天才,才能胜任的工作。

但是!也请放心!
公司也不可能让我们程序员去做这件事的。
一般都是外聘的专业人员来操作的。
存储过程,用的最多的就是 医院。
医院需要对每天,每月,每年,进行数据(收入,考勤。。。)汇总(各种报表)。
总之涉及到数据,非常多。
这就需要从各个数据表中 抽取数据。
此时 简单的SQL语句,就搞不定了。
这个时候,才会用到存储过程。
?
由于存储过程 的难度,非常大!
对人要求很高,随之而来的就是高额的工资。
大概是我们程序员工资的 1.5 倍以上。
即:招一个程序员约定1万工资,那 DBA 的工资,至少是 1w5 以上,要不然你找不到人的。

高级映射
高级映射,除了可以实现 数据表 和 程序里面的对象 映射之外,还可以实现 一对一的多表映射 和 一对多的映射。
?
比如: CSDN
每一篇博文,都有它的作者。
博文 与 作者 的关系,就是一对一的关系。
此时,使用 MyBatis 就可以在进行查询的时候,进行 连表查询,并且将 查询到的数据结果,映射到 我的某一个 文章对象里面。
?
再来,一个作者,他可以发很多篇博文。
站在这个角度,作者 与 他发布的博文,成 一对多的关系。
这种情况,使用 MyBatis 也可以实现这个任务。
MyBatis 可以直接去查询数据库里面的数据,然后,将这些 数据 赋值 给 UserInfo。
这个 UserInfo 里面是有一个属性的(list article - 文章列表)。
就是用来记录这个作者创作的作品。
MyBatis 去除了?乎所有的 JDBC 代码以及设置参数和获取结果集的?作。
以往,我们在进行 JDBC 编程的时候,需要获取数据源对象(DataSource),然后设置三个属性 URL(数据源路径),用户名(默认是 root),密码(MySQL的登录密码)。
在设置完这三项之后,然后才能与数据连接,进行一系列的操作。
?
另外在获取 结果集的时候,我们需要使用到一个 类(ResultSet)的对象,来接收数据库返回的结果集,还需要通过 迭代器的方式,才能进行后续的打印。
?
使用了 MyBatis ,上鞋这些就都不需要了。
它会自动帮我们实现映射,
我们只需要写 SQL 语句(不要写错),写完之后,它会把这个 SQL语句 执行,将 查询到的结果(表数据),直接全部映射到对象里面。
也就是说: JDBC 的前置操作的代码,我们一行都不需要写。
MyBatis 直接全包了,你直接用就行。
特别爽!

MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接?和 Java POJO(Plain Old Java Objects,普通?式 Java 对象)为数据库中的记录。
MyBatis 在实现的时候,它有两种实现方式:
1、通过 xml 来实现数据的操作,数据操作支持所有的类型。
这是在 MyBatis 3.1 版本之前,主流的一个操作方式。
使用 xml 的形式,来实现 对数据库进行 “CURD” 操作。
?
但是呢!在3.1 版本之后,MyBatis 有了新的选择。
也就是 第二种方式(注解方式)。
但是目前主流还是使用的 xml 形式。
为什么呢?
我们不说 MyBatis 使用起来,非常灵活!
灵活是需要付出代价的!
代价,就是我们要去写 SQL语句。
你试想一下:我们在注解中写一个 SQL语句,是不是很别扭?
代码没多少,注解倒是非常长。
所以,MyBatis 在 3.1版本之后, 提供了 注解 这个新的操作数据方式。
它也没有用,因为不好用。
注解的方式,用来实现简单 SQL 操作,还是可以的。
但是!一旦SQL语句复杂了一点,你在使用的时候,就会特别别扭。
注解 比 代码,还有长。。。
而 xml 写SQL语句比较灵活,也不会感觉到别扭,好用!
?
所以,本文也是 基于 xml 来实现 对数据库 进行操作的。

?
为什么要学习 MyBatis
对于后端开发来说,程序是由以下两个重要的部分组成的
1、后端程序
2、数据库
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?这两个重要的组成部分要通讯,就要依靠数据库连接?具,那数据库连接?具有哪些?
?如之前我们学习的 JDBC,还有今天我们将要介绍的 MyBatis,那已经有了 JDBC 了,为什么还要学习 MyBatis?

这是因为 JDBC 的操作太繁琐了,我们回顾?下 JDBC 的操作流程:
1、 创建数据库连接池 DataSource
2、 通过 DataSource 获取数据库连接 Connection
3、 编写要执?带 ? 占位符的 SQL 语句
4、 通过 Connection 及 SQL 创建操作命令对象 Statement
5、 替换占位符:指定要替换的数据库字段类型,占位符索引及要替换的值
6、 使? Statement 执? SQL 语句
7、 查询操作:返回结果集 ResultSet,更新操作:返回更新的数量
8、 处理结果集
9、 释放资源
实例代码 - insert 操作 - 截取自 我自己的文章内容。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

从上述代码和操作流程可以看出,对于 JDBC 来说,整个操作?常的繁琐,我们不但要拼接每?个参数,?且还要按照模板代码的?式,?步步的操作数据库,并且在每次操作完,还要?动关闭连接等,?所有的这些操作步骤都需要在每个?法中重复书写。于是我们就想,那有没有?种?法,可以更简单、更?便的操作数据库呢?
答案是肯定的,这就是我们要学习 MyBatis 的真正原因,它可以帮助我们更?便、更快速的操作数据 库。
之前使用 jdbc 需要进行九步操作,现在使用了 MyBatis,就写一个方法,一个 SQL就搞定了。 像什么手动释放资源,处理结果集,都不用我们去做了。MyBatis “保姆级” 助手。

?
怎么学 MyBatis?
本文重点分为两个部分:
1、配置 MyBatis 开发环境(创建一个 SSM 项目)
2、使用 MyBatis 模式和语法 操作数据库

?
1、创建 MyBatis 项目 准备工作:创建数据库 和 数据表
下面我们会创建一个关于 博客 的 数据库 mycnblog
并且在 mycnblog 数据库中创建三张表。
代码给你们了,戴氏!里面暗藏 “杀机”。
会导致 产生一个 BUG,不然有解决的方法,
先就这么弄。

不过有一点需要注意!
在 文章表中的 content字段,它的类型是 text。
text 能够表示文本内容有限,在实际情况中,一般是使用 longtext。
另外, createtime 字段,我们使用了一个 now 方法,这个 now 方法,需要 MySQL 的版本,在5.5以上。
不过应该问题不大,毕竟我们 与 jdk1.8 配套的数据库版本,是 5.7版本的。
?
你仔细看下面创建 SQL,无论是创建 数据库,还是数据表,我都设置了 字符集为 utf8。
确保一定是支持中文的。
这样做的好处,就是:哪怕你们没有修改数据库的配置,创建出来的表,仍是是支持中文的。
?
还有一个小细节:在添加一个用户信息的SQL语句中,我们的密码是明文存储的。
这样去写,势必会遭到 面试官的吐槽。
即使你使用了 md5 进行加密,其实也没有多大作用。
如果只是一个 md5,其实我们可以通过 “彩虹表” 进行穷举,来获取你的密码。(注意!这不是解密,是穷举你密码的可能性)
有人肯恩就会想法:
密码 在一次 md5 的情况下,能够通过 彩虹 表 来穷举 获取密码。
那我们 对 密码 md5 两次,甚至更多呢?
那 彩虹表是不是就破解不了了??
你都能想得到,别人黑客就想不到吗??
别说两次,就算你 3次,4次,甚至是五次,人家 应对方法的。
而且,无论你md5加密几次,其实就是累加的效果,对于黑客来说无非就是多 穷举几次而已。
最靠谱的方式,就是有随机数的,有变量的。
这样做更靠谱!
因为是随机生成的嘛,没有规律可言!
想要获取密码,就非常难!
几乎是不可能的。

-- 创建数据库 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 '', 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 ); -- 创建视频表 drop table if exists videoinfo; create table videoinfo( vid int primary key, `title` varchar(250), `url` varchar(1000), createtime datetime default now(), updatetime datetime default now(), uid int ); -- 添加?个?户信息 INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) VALUES (1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1); -- ?章添加测试数据 insert into articleinfo(title,content,uid) values('Java','Java正?',1); -- 添加视频 insert into videoinfo(vid,title,url,uid) values(1,'java title','http://www.baidu.com',1);

打开你们本地的MySQL,将上述SQL,复制粘贴到 MySQL中。
PS: 建议先拷贝到 一个文本文件中,然后再拷贝到 MySQL中。
因为你如果直接拷贝上面的内容,SQL就会挤成一坨。
也就是说不带有格式的。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

你把上面代码先放在笔记本中,再粘贴到MySQL中。
不但没有挤在一起,而且SQL全部执行了,只有最后一句SQL没有执行。
直接回车,就全部OK了。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

我们下面来看看数据,有没有插入成功
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
1.1、添加 MyBatis 相关依赖
眼尖的朋友,会发现我使用了 “相关” ,这个形容词。
没错,我们要想使用 MyBatis,光引入 MyBatis 依赖,是不够的。
我们需要引入一个依赖(关于数据库的)。

这里就会涉及到两个场景:
1、项目创建的时候,引入 MyBatis 相关依赖
2、老项目,添加 MyBatis

?
1、新建一个 MyBatis 项目
?
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

然后,就是将 无用的文件,删除掉。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
2、在老项目中引入 MyBatis 相关依赖
这个讲了很多次,使用 Edit Starter 插件 添加。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
1.2、配置数据库连接字符串
不要立即启动项目!!!!
如果立即启动项目,就会报错。
指针对 社区版 idea,专业版会帮助我们自动生成关于 数据库的 配置信息。

社区版:
这是因为 没有配置 关于 数据库的数据源的 URL
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
专业版:
直接就给我们配置好了。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

现在我把的配置文件删除掉,重新创建一个配置文件,与社区版保持一致,我们来重新编写配置信息。
我们需要配置 4 项:
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

此时,我们再启动项目,就不会报错了。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
1.3、配置 MyBatis 保存的 xml 的目录
我们前面说过 MyBatis 有两种 操作方法:
1、使用 xml 的形式 实现呢
2、 注解(MyBatis 3.1版本之后,开始提供)
但是!
注解的方式,并不好用。
因此,我们主要还是关注 xml 形式,是如何操作 数据库。
?
思考一下:
xml 是一个 资源文件,对不对?
那么,xml 文件,还是放在 resource 目录下。
还有一个问题:
放在 resource 哪个 目录底下。

需要注意的是:一般是不会将其放在 resource 根目录底下,与配置文件处于同一级目录。
因为到时候,配置文件,非常多!
此时,你放进去,不就是添乱嘛!!!
?
通常我们都是在resource 目录下,创建一个子目录,用来存放的。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
使用 MyBatis 的操作模式 操作数据库 MyBatis 的操作模式 JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

MyBatis 的 操作模式,包含两个部分:
1、Interface(方法定义)
2、xxx.xml

注意!这里是接口,不是类。
这个interface 中 会加一个注解,它是来自于mybatis里面的注解 Mapper。
加了这个注解之后,表示我们当前的这个 mybatis 类 所的事情,就是 实现 对象的映射。
这个时候,帮我们的方法名字 定义到 interface 里面。
我们 interface 里面的方法,是不能有实现的,是一个抽象方法。
?
思考一下:
假设,我们要根据用户ID 去查询一个用户信息。
那我们定义一个方法名有什么用?又没有具体实现,怎么可能会实现查询功能??
答案显而易见是不行的!!!
'这个时候,我们就需要另一个东西:xml 文件
mybatis 有点奇怪,感觉就像是 “怕脑门” 想出来的方法 。
就是 要想操作数据库,必须得有两个文件。
第一个,它是Java中的 一个普通接口,在接口中,定义方法名称。
定义方法名称之后,就该实现方法了。
但是接口中方法不能有实体,需要通过一个注解来进行映射方法,映射一个 xml 文件中。
当然,在xml文件中,也需要表明它是那个方法的映射。
这就是第二个文件 xml。
根据我们前面讲的映射:就是 把业务代码 转换成 SQL 语句。
也就是说我们 定义在接口中的方法,是为了声明该方法 对数据 进行 何种操作。
具体的实现 已经通过某种映射关系,映射到 xml 文件中,
我们需要在 xml 文件中,编写 对应功能的 SQL 语句,就可以了。
?

有的人可能会疑问:
我们可不可以将 SQL 语句 写在代码中,就像 jdbc 编程 一样。
答案:可以,但是!SQL 只能是 String 类型的,如果SQL写错,它是不会报错的。
这就很容易翻车。
所以,mybatis 的设计师 思考一会,这样的话要不直接把 SQL 写在 xml 里面?
于是一拍大腿,就这么决定了。
在xml里面,只需要配置 我们要是实现的 interface 是谁,我要实现的 SQL 是谁。
OK ,这样一配置就完了。
这两个合在一起,最终生成 mybatis 中能执行的SQL。
通过这个SQL语句,去操作数据库。
将 操作数据库得到的结果,返回给 服务层。
服务层,再返回给 controller(控制层)。
控制层,再把结果交给用户。
这样 就完成了 一次交互。

所以,我们讲 mybatis 的 操作模式,就是在讲下图中的两个文件。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

这两个配合起来,就能生成 数据库可以执行的SQL语句,并且执行 SQL,操作数据库。
并将 结果 映射到程序的对象中。

?
第?个MyBatis查询:实现一个根据用户id来查询用户信息的操作
我们要实现一个 根据 用户id 来查询用户信息的操作。
前面我们在准备工作中,就是已经给 userinfo 表中插入了一个数据。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

下面,我们就来完成这样的一个功能。

?
1、定义接口
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
2、创建 xml,实现上面的接口
xml 文件,不能随便创建。
我们在配置文件中,已经指定了 xml 文件存储路径。
并且,命名规则也指定了。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

我们必须按照规则来。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

至于 xml 文件的配置内容,直接把下面的内容,拷贝到里面去。


相信很多朋友,都会发现 mapper标签中的 namespace 属性,缺少value值。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

namespace 的值,是需要我们手动去填写的。
填写的内容:是需要时实现的接口位置 >> 包名 + 接口名称。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

接下来,就是在 xml 文件中,实现 UserInfoMapper 接口 中的 方法。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

关于获取方法参数,使用的 ${} 和 #{} 的区别,后面会详细讲。
&enso;
好,关于 MyBatis 的操作,就都完成了。
下面,我们就可以来验收成果了。

?
运行结果展示
我们在 service 包底下创建一个 UserInfoService 类。
在里面 写一个方法, 去 调用 UserInfoMapper 中的方法。
然后,在 controller 包下,也去创建这样的一个类,去调用 UserInfoService 中的方法。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

此时,我们将项目启动起来。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
注意!有的人可能会在这一步出现问题
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

我被坑的头皮发麻。
第一次遇见版本不匹配,引起的错误。
看异常信息检查不出错误。
出现这个问题的原因是:我们添加的 MyBatis r的版本太高了,而我们使用的 Spring Boot 版本太低了(阿里云提供的)。导致的版本不兼容。
因此 URL 不能像正常情况下,那样去配置。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

应该这样去配置
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

更简单解决方法的是:使用官方 提供的Spring Boot 框架,选择次新的。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

我使用的是 阿里云,所以 版本很低,我是用的版本是 2.3.7。
所以,才会出现上述情况。

?
MyBatis 执行过程
在这里,我在补充一点。
我们在写 mybatis 代码的时候,无非就是两个操作:1、创建接口,定义方法;2、在写满了文件中编写SQL。
整个 mybatis 操作 完成了。
?
这里有一个比较麻烦的地方:
就是在我们写完功能之后,我们要进行测试的话,成本很高!

mapper的内容写完,我们还需要写 service,写 controller,还没完!
我们还需要打开 浏览器方法(或者利用postman来模拟访问)。
虽然整个流程,非常有序,一环接一环。
但是!这会让人感觉很拖!
给人的感觉就是:代码被强行 “ 拉长 ” 了。

那有没有什么方法,能让我们写 mapper 内容之后,直接可以进行测试呢?
这就是下面,我要讲的东西:Spring Boot的单元测试

?
Spring Boot的单元测试 - 穿插内容
讲这个,是为了后面讲解 Mybatis 的内容。
1.什么是单元测试?
单元测试(unit testing),是指对软件(项目)中的最?可测试单元进?检查和验证的过程就叫单元测试。
单元测试是开发者编写的??段代码,?于检验被测代码的?个很?的、很明确的(代码)功能是否正确。
执?单元测试就是为了证明某段代码的执?结果是否符合我们的预期。如果测试结果符合我们的预期,称之为测试通过,否则就是测试未通过(或者叫测试失败)。
?
最小可测试单元:方法
每一个方法都代表一个相应的功能。
那我们测试的最小单元,对于 Spring Boot 来说,就是一个方法。
OK,你写一个方法,我测一个方法。
测试方法,就是测试一个单元。
方法,是不可以再被分割的!
你不可能说: 我们去测试 方法中的某一段程序,或者说测试某一个属性。
因为,这叫做 调试。
调试 和 测试单元,是两码事!!!!
?
但是!有的朋友可能会说:
方法中,经常会调用另一个方法,来“辅助”自身的运行。
那这种怎么说?
如果方法中还有方法,这个时候,单元测试就需要分为多个了。
首先是这个方法中所有依赖的方法,我们可以对这里面所有的方法,做一个单元测试。
最后,再回到本身,对本身的方法进行单元测试。

?
2、单元测试有哪些好处?
1、单元测试不用启动 Tomcat
对于这一点,不好说。这个说法,要分角度来看。
从自身的角度来看:我们确实没有启动 Tomcat,但是 Spring Boot 自动启动了 Tomcat。
因为 Spring Boot 内置了 Tomcat。
其实 Spring Boot 除了支持 Tomcat,还支持其它的Web容器。
2、如果中途修改了代码,在项目打包的时候会发现错误,因为打包的时候会自动执行单元测试,单元测试错误就会发现。
这一点最重要!
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

3、单元测试,最大的一个好处 :
让我们非常方便,非常直观,也非常直接的,在写完一个功能之后,立马就能知道这个功能是否是正确的!
这也是我们学习单元测试的初衷,就是为了降低 测试 mybatis 功能的成本。
?
4、不使用单元测试的时候,它是会 “ 污染 ” 本地的数据库的!
就是比如说:
我们实现了一个 添加 功能,我们要去测这个添加功能。
这就需要正儿八经的去进行添加操作的。
这就会对本地数据库中的数据,发送变动。
这就是 “污染”!

然而,如果使用了 单元测试 来测试功能,.
测试单元,就能保证在 测试完功能之后,对于数据库中的数据没有任何影响!

这个牵扯到了 事务的回滚机制。
这是 我在讲 MySQL是如何保证事务的原子性 的 时候,提到过的 事务回滚机制。
有兴趣,就自己去看,目录给你标出来了。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

单元测试,也是通过利用 数据库的回滚机制 来 避免对 数据产生 “ 污染 ” 。
也就是说:我们在执行单元测试之前,MySql 为其创建了一个事务。
【MySQL 默认情况下,是处于开启事务的状态,也就说开启了事务的自动提交】
然后,开始执行单元测试,对数据库中的数据进行操作。
在验证完 单元的功能没有问题之后,就会进行事务的回滚操作。
还原到 还没有测试的情况。【将数据还原到初始状态】

?
3.Spring Boot 单元测试使用
Spring Boot 项?创建时会默认单元测试框架 spring-boot-starter-test,?这个单元测试框架主要是依靠另?个著名的测试框架 JUnit 实现的,打开 pom.xml 就可以看到,以下信息是 Spring Boot 项?创建是?动添加的:
PS:我用来演示效果的项目,是我一步一步带着大家创建的。
所以,作假含量极低。我可没有偷偷添加这个依赖!
都是 Spring Boot 干的!!!雨我无瓜!
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

这里有一个细节:
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

&enspp;
单元测试的实现步骤 1.?成单元测试类
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

这个时候,此?法是不能调?到任何单元测试的?法的.
此类只?成了单元测试的框架类,具体的业务代码要??填充。

?
2 添加单元测试代码
1.添加 Spring Boot 框架测试注解:@SpringBootTest
至于为什么还需要加一个 测试注解,是因为 我们生成测试类,是一个普通的类。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

但是!测试接口,它是一个普通的接口吗?
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

因此,我们需要在测试类上 声明它要测试的方法,是运行在 Spring Boot 容器当中的。
所以,@SpringBootTest 注解,就是起着这样的一个声明作用。
表示当前这个类中 测试方法(测试单元) 是运行在 Spring Boot 中的。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
2.添加单元测试业务逻辑 JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
3、开启测试
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

虽然,从打印的结果来看,功能是没有的。
而且,左边那几个 绿色勾勾,代表测试通过。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

但是! 我们是使用 的 sout 语句,来对查询结果进行输出。
这并不是 我们 单元测试的效果!!!!
单元测试的效果应该是 单元测试中的断言!!!

断言:
就是说:以什么为标准,
通过这个标准来衡量 测试是否通过。
比如,如果 断言的结果为 true,就表示 单元测试 通过了。
反之,断言的结果为 false,就表示 单元测试 不通过。
并且!断言之后的代码,是不再执行的!!!
所以。还没有完。
单元测试,还有一部分是关于 断言 的 内容。
我们是需要知道的。

?
4、简单的断?说明
由 Assertions 类提供的,注意!这个类是 JUnit 提供的
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

下面,来跟着我看一下,如何使用断言,来判断单元测试是否通过!
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

这就是断言的魅力,一旦出错。
如果测试失败,它的报错信息会非常非常的详细!
?
后面,我们实现 增,删,该2的时候,就不需要 浏览器 和 postman了。
直接使用断言,来测试即可。
?
这里做一个小拓展:
为什么有些人的 科学版 idea, 在进行 与 @Mapper 相关的属性注入,使用 @Autowired 来注入,为什么会报错?
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
拓展:“小鸟” - 插件 MyBatisX
它能够帮助我们快速切换 接口 和 xml。
让我们操作 MyBatus 更舒服。
而且,还能帮我们自动去生成代码。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
增、删、该操作
接下来,我们来实现?下?户的增加、删除和修改的操作,对应使? MyBatis 的标签如下:
< insert >标签:插?语句
< update >标签:修改语句
< delete >标签:删除语句
?
接口的定义,还是一样的写法。
写法的差异,主要就体现在 xml 中 的 自定义 SQL 语句 上。

?
MyBatis 修改操作
我们先给 userinfo表插入一条记录。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

下面,实现 改操作。

下面我们来拓展几个细节:
1、方法参数 使用 @Param 注解之后,原先参数的名称不能再使用。

得出结论:
@Param 注解 和 @RequestParam 注解,起着相同的作用。
方法 参数重命名。一旦重命名之后,原来的名称是不能使用的。

只不过 @Param 注解中的参数是 重命名后的结果。
而 @RequestParam 注解中的参数 是参数原名,方法参数是 重命名的结果。
于此同时,我们又发现了一个问题。
当我们测试完之后,我发现此时数据中的数据被 “污染”了!
前面不是说:单元测试不会污染 数据库中的数据嘛?
下面,我们就来实现这个功能。
【免得你们认为我是个老六,说我忽悠你们】

?
在不修改数据库中数据的情况下,完成单元测试 - 穿插
方法很贱简单,在测试的单元上,加上一个注解@Transactional,就行了。
@Transactional:就是事务的意思。
其作用就是在开始执行测试之前,开启一个事务。
这个事务完成的任务,就是我们定义的是SQL操作。
在 操作完成之后,也就是测试完成之后,该事务会进行一个 “ 回滚 ” 操作。
原来的数据是什么样子的,现在还是什么样子的。

就是说:事务执行的期间确实 “ 污染 ” 了 数据库中的数据,
但是!在事务执行完成之后,事务会通过 “ 回滚 ” 操作,将被 操作的数据还原到未修改的状态。
?
这里需要注意一点:
@Transactional 注解,原本只是提供一个 自动提交事务的功能。
并不会提供 自动 “ 回滚 ” 功能。
只是说 该 单元测试 所处于的 测试类,是被 @SpringBootTest 注解 修饰的。
等于就是说:“强行” 给 @Transactional “ 加班 ”!!!
无论 单元测试是否成功,都会进行一个 事务的 “ 回滚 ”,将数据库的数据还原!
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

我没有晃点你们吧?
只需要在 测试的单元上,再加上一个 @Transactional 注解,就可以在 “ 不污染 ” 数据库中数据的前提下,完成单元测试。
?
当然,这个操作的大前提:
是在 测试的单元所在的测试类,是被 @SpringBootTest 注解所修饰的。

?
MyBatis 删除操作
有了 修改操作,删除操作实现起来,就简单很多了。
?
1、在 mapper 中的 UserInfoMapper 接口中定义 方法
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
2、在xml文件中 编写 SQL语句 的相关代码。
注意!只有 select 标签,特殊一点(需要指定返回类型)。
delete 标签 和 update 标签都是一样的。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
单元测试 - 验证功能效果
【JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔】JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

有了 修改 和 查询操作的基础,这个删除操作,就跟玩一样。
很快就能搞定!

?
MyBatis 插入操作
这个插入操作,相比前面 “ 删查改 ” 操作,要复杂一些。
所以,把它放在最后来讲。
操作还是一样的,只是插入的值,是一个对象,在 编写 SQL的时候,如何获取对象的属性,是一个问题。
还有返回值的处理。

?
先实现最简单 的 插入操作
返回值,返回一个受影响的行数
1、在 mapper 中的 UserInfoMapper 接口中定义 方法 JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
2、在xml文件中 编写 SQL语句 的相关代码。 JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
3、进行单元测试 JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

算了,为了防止你们认为我做假。
我 “污染”一条 王五的信息。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
上面最简单的实现方式,下面我们来看难一点的实现。
现在我们不想 将 影响的行数,作为返回值了。
我要 插入成功的用户信息的ID!
虽然实现的步骤没有变化,但是想要达到预期的效果,还需要做点事。

?
先来看第二步:在xml文件中 编写 SQL语句 的相关代码。 JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

对了,我还扩展一下。
在上述 insert 语句中,最好加一个属性:keyColumn。
更保险!
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

useGeneratedKeys:
这会令 MyBatis 使? JDBC 的 getGeneratedKeys ?法来取出由数据库内部?成的主键
(?如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的?动递增字段),默认值:false。
?
keyColumn:
设置?成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第?列的时候,是必须设置的。如果?成列不??个,可以?逗号分隔多个属性名称。
?
keyProperty:
指定能够唯?识别对象的属性,MyBatis 会使? getGeneratedKeys 的返回值 或 insert 语句的 selectKey ?元素设置它的值,默认值:未设置(unset)。如果?成列不??个,可以?逗号分隔多个属性名称。
?
简单来说:我们加上 keyColumn 的原因,就是为了确保获取 对应字段。

?
再来看一步:在 mapper 中的 UserInfoMapper 接口中定义 方法。
返回受影响的行数 和 自增 id
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
我们测试一下。
带了这一步,我就不给你们演示如何创建 单元测试了。
这都soEeasy的事情了。
另外,我直接上代码。
看不懂,就返回去看前面的。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

有的人可能会有疑问:自增ID,是有什么问题吗?
其实没问题!
这是因为 MySQL 中的 搜索引擎 InnoDB 的机制,就是这样设计的。
?
学到这里,就可以将一些 简单的servlet 项目,改成 SSM 项目。

?
查询操作 单表查询
这个就是前面的 第一个 MyBatis 查询 。
它急救室一个单表查询。

单表查询,就是字面意思:
在一张表中查询数据。

这个我就不重复讲了。
来看下面的重点

?
参数占位符 #{} 和 ${} 的区别
#{}:预编译处理。
${}:字符直接替换

预编译处理是指:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使? PreparedStatement 的 set ?法来赋值。直接替换:
而MyBatis 在处理 ${} 时,就是把 ${} 直接替换成变量的值。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

这里需要普及的一点:
MySQL中有两种查询:1、叫做及时查询(字符直接替换);2、预查询(预处理)
及时查询:当了需要执行SQL的时候,才会去执行SQL,
预查询:相对于 及时查询,预查询要多出两步:既然查询的参数会变,那先用一个 ?号占位符 顶替参数,先构造好一个SQL,进行预查询,看看SQL的格式有没有错误。
随后,通过 jdbc 的方式来替换 占位符。这样就能保证SQL的正确性。
?
你可能觉得这麻烦,但是其实!预查询是为了提高查询效率的。
举个例子:

通过上述的代码,只能证明 #{} 和 ${} 对于 查询条件为整形的查询效果是一样,至于效率,只能意会,毕竟,我们感知不到。
但是!安全性,是可以体现的!下面我们就来演示一下。

由此,不难得出结论:#{}支持所有的数据类型,${} 只支持 数值类型
再举个例子,来加深对 预编译【 #{} 】和字符串替换【 ${} 】之间的区别 的理解
?
头等舱和经济舱乘机分离的故事:
在坐?机的时候头等舱和经济舱的区别是很?的,如下图所示:

?般航空公司的客运机都是头等舱和经济舱分离的,头等舱的?先登机,登机完之后,封闭头等舱,然后再让经济舱的乘客登机,这样的好处是可以避免有人浑?摸?,经济舱的?混到头等舱的情况,这就相当于预处理,可以解决程序中不安全(越权处理)的问题。
?
?直接替换的情况相当于,头等舱和经济舱不分离的情况,这样经济舱的乘客在通过安检之后可能越权摸到头等舱,如下图所示:
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

这就是 直接替换【 ${} 】 所带来的缺陷。
就是说: SQL 没有正确访问它应该访问的地方。
这就是 SQL注入的安全问题。

而且,由于头等舱的票很贵,买的很少。
通常是坐不满的,因此,上述这种 经济舱的人 做到 头等舱也是常有的 。
最好的方法,就是前面说的一下,先让头等舱上机,上完之后。
关闭头等舱的入口,然后,经济舱的人开始上机。
这样就不会存在有 “ 误入 ” 的情况了。
?
有些人的素质是真的低,买了低价的票,却想着享受高价的服务。
如果不让他如愿,它还恶意举报 航空公司。
所以,出现这种问题的根本原因:就是等级没有分明。
头等舱的门,就相当于是一个界限。
将 头等舱 与 经济舱 分离了。

有的人可能会说:加一个检票员来检查票据。
这样不也能解决问题嘛。
但是!你遇到不讲理的怎么办?
它就是要坐!
它有一大堆歪理由。。。。
这时候,你想把它请出去,非常难!
容易产生纠纷。
所以,不推荐这样去做。
?
因此,正如前面所说:在登机的时候就把 人员分开。
让优先级高的线上,就不会存在纠纷问题。
同样也很高效。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

这样就不用做第二次校验,也就不会产生纠纷问题。
即使 头等舱的用户来迟了,叫一个空乘服务人员,专门领他过去,就行了。
领进去,再把门一关。
经济舱的人还是混不进入的。
?
也就是说:每个人都只经历了一次检验机票,就完成了登机。

那么,问题来了。
既然 #{} 这么无敌,那么,${} 是不是就可以滚蛋了?

NO!
存在,即有意义!【蚊子除外!】
下面,我们来看 ${} 的使用场景。

其实,与其说是 ${} 的优点,不如说是它的专长!
毕竟 这任务,#{} 做不了,只有它能做!

前面不是说:
使用 ${} 存在 SQL 注入的问题嘛?
现在,哪怕是处于 ${} 的主场,依然也存在着这个问题!
那如何处理呢?

注意事项:
当不得不使用 ${} 时,那么一定要在业务代码中,对传递的值,进行安全校验!
就是说:当代码执行到 mapper 这一层,就没救了!
只能 “ 上战场 ”了。。背水一战。
所以,一定要在业务代码中,对数据校验。
Controller 的作用就体现出来了!
因为 它就是负责对数据的校验。
结合现在的情况:就是对 传递过来的 order 进行 判断非空,长度不能为0,甚至对内容进行校验,是否是 desc 和 asc,其中的一个。
?

结合生活案例:
我们在上地铁,火车,飞机等公共交通工具的时候,为什么在我们乘坐之前,就开始检查我们的包裹,而不是 上车的时候,检查包裹。
这我们的 ${} 的校验数据,是同一个道理。
为了保证传递的数据的安全性以及正确性。【确认没有违规物品,保证乘客的声明安全】
?
${} 的经典问题:SQL注入问题
由上面的例子,相信大家对 SQL注入问题,有了一定了解。
SQL注入问题,就是在我们没有对 传递数据进行校验的前提下。
当我们处于一些特殊场景的时候,可以在不满足获取条件的情况下,获取私密信息。
这对用户信息和 财产的安全隐患是非常巨大的!!!
这就是安全漏洞啊!!!
?
所以,请一定一定一定切记!在处理一些重要信息的时候,如果使用了 ${},请在 Controller 层,对数据进行严格校验!
?
我们再来试一下:把 $ 改成 # ,效果会如何?
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

由此,不难得出结论:
使用 #{} 是不存在 安全漏洞滴!!!
是非常nice的。

?
like 查询 - 特殊情况
前面的问题,由于关键字,就那么几个。
直接穷举,很容易在 Controller 层里面 判断数据的正确性。
?
但是!模糊匹配能穷举吗?
很显然是不能的!
如果数据有个几百万,那我们不得嗝屁!
下面,演示一下。

此时,问题也就随之而来了!
在进行模糊匹配查询的时候,不能使用 #{} ,否则会报错!
而改用 ${} ,有穷举不了所有的情况。
无法做到完美验证数据的正确性和安全性。
?
那么,怎么解决这一个问题呢?
使用concat拼接方法,就可以解决问题了。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

?
#{} 和 ${} 的区别总结
1、定义不同:#{} 预处理:而 ${} 是直接替换
?
2、使用不同:#{} 适用于所有类型的参数匹配;但 ${} 只适用于数值类型。
?
3、安全性不同:#{} 性能高,并且没有安全问题;但 $ {} 存在 SQL 注入的安全问题。
?
4、使用场景不同:
当传递的是一个 SQL 关键字 的时候,只能 使用 ${} 。
当传递的是一个字段,总之,就是需要获取到参数类型 与 内容,只能使用 #{}。
(PS:数字类型,#{} 和 ${} 都是可以使用的!)
?
5、 ${} 不能用于 模糊匹配查询;而 #{} 需要搭配 concat 才能在模糊匹配中使用。

?
多表查询 前置知识
返回类型:resultType
这个大家都很熟悉,我们已经在前面的示例中,使用了很多次。
用于指定 SQL 查询结果 映射的对象类型。
但是!这里有一个细节!
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

除了修改 实体类属性名称,还有一个方法:使用 下面即将介绍 resultMap。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

返回字典映射: resultMap
resultMap 使用场景:
1、字段名称和程序中的属性名不同的情况,可以使用 resultMap 配置映射、
这个上面演示过了,就不再演示了。
?
2、一对一和多对多关系可以使用 resultMap 映射并查询数据。
?
resultType && resultMap 的区别
1、在对象属性名称 与 数据表字段名称,相同的情况下:
使用 resultType 比 resultMap 更爽!
?
2、在对象属性名称 与 数据表字段名称,不同的情况下:
使用 resultMap 可以指定 不同名称的 字段与属性 的映射关系。

?
有了 resultMap 和 resultType 的基础,我们下面就可以真正开始进行多表查询的操作了。
在 MyBatis 中,支持的多表查询有两种:
1、 一对一关系:以博客来说,一篇博文只能有一个作者,文章和作者的关系就是一对一。
2、一对多关系:一个作者可以是 多篇博文的作者。都是他写的嘛!作者 和 文章是 一对多的关系。

?
MyBatis 多表查询:一对一关系
下面,我就来模拟实现一下 一对一的关系。
but!在此之前,我们需要创建关于文章表的实体类。
我们前面只是创建一个 用户表的实体类。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

在进行查询之前,我们先来看一下 articleInfo 表中,有几条信息,信息的内容是什么,这样方便我们后面写代码。

JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

如果你们的content 出现乱码,反正就不是中文。
你就可以参考这篇文章MySQL第二讲,在文章的最后讲了如何配置字符集的问题JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

下面,我们去mapper 包中,在创建一个 MyBatis 的接口。
实现根据文章的 id 查询到文章的详情信息。

咋一看,好像没有问题。但是确实有问题!
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

这就很好奇了,文章的 uid 是 1,对应着 用户表中 admin 用户。
就是说:文章的作者,确实是存在的。
那为什么 userinfo是 null 呢?
原因很贱,我们的实体类有这哥属性,但是文章表里没有这个字段啊!
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

这个时间,resultMap 就上场了。因为 resultType 它不行!

可参考对数据表进行“增删查改”的进阶操作
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片


此时,我们就实现了一对一查询。(存在问题)
【association 标签,就是用来实现一对一情况的多表查询】
虽然存在一些问题:
UserInfoMapper 中 resultMap 配置的字段信息不完整。
导致:虽然查询结果的信息是完整,但是没有完整映射到 ArticleInfoMapper 的 userinfo 属性中。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

这里可以体现出一个结论:
在 本身 xml 文件,是可以不用映射所有属性的信息。
因为 它是自己调用自己,所以,不需要 resultMap 将属性全部映射,都能自动完成所有属性的映射。
而 想要在一个 resultMap 中 调用 另一个 resultMap 中信息,只能是它映射了的信息。
否则无法获取。
所以,我们来讲 UserInfoMapper 中 resultMap 对于属性的映射补全,再来看个效果。JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

这里还存在着问题:
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

到头来,结果发现:还是存在问题的。
当一个属性,在两个数据表中都存在时,默认读取的是 本身的字段值。
也就是说:不光是上面的两个,我们演示的那个情况,出现的那四个属性都是这个情况。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

下面,我们来分析解决这个问题。
同时加深对这个问题的印象。

这个时候,我们才真正完成了 一对一关系的多表查询!!!!!

?
MyBatis 多表查询:一对多关系
一对多关系:?个?户可以是多篇?章的作者。
?对多需要使? < collection > 标签,?法和 < association > 是一样的 .

为什么使用 collection标签,其实很理解。
看中文意思就明白了!
association 的中文就是关联,放到使用场景中,就是将 2个表联系起来。
?
collection 的中文意思就是 收集(把它理解为集合),将多个表收集起来,整合。
当然,这些表,肯定都与一张表有关系,不管怎么叫做 一对多?
放在实例中,多个表对应着多篇文章,
这多个表中有一个相同字段,且信息是一样的。【作者id是一样,即是同一个人写的】
由这个作者id,就可以把所有文章(是他写的文章)给映射起来,建立关系。
这就是 一对多 关系。

在实现一对多关系查询之前,我们先来做一些准备工作。
首先我们来用户表的实体类进行处理。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

下面我就来实现这个一对多的关系查询。。
实现一个 根据用户id,获取用户和他缩写的文章信息。

换句来说:
一对多关系的查询,相比于 一对一关系的查询,就是把 resultMap 中的 association 标签,换成了 collection 标签。
里面的内容一点都没改。

?
复杂情况:动态SQL使?
动态 sql 是MyBatis的强?特性之?,能够完成不同条件下不同的 sql 拼接。
可以参考官??档:Mybatis动态sql
进去之后就是这个页面
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

你会发现 动态SQL,被拎出来,放目录里,说明什么?
说明它重要啊! 不然,我也不会给它一级标题啊。

?
动态标签 - < if >标签
在注册?户的时候,可能会有这样?个问题,如下图所示:
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

回顾我们之前实现的功能查询,你会发现参数全部都是确定的,就是写死了的。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

都是抱着那种前端一定会传给我数据的 “ 态度 ”,也就是认为一定会拿到必要参数!
但是!在实际业务场景中,会存在一些非必传的参数。
就是说:必要参数,有时候是不会传输的,很有可能传过来的参数,是没有的。
那么,面对这种情况,我们在程序中该如何处理?
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

下面,我们就来具体实现这个业务。

但是存在着一个问题:如果参数很多,而且!每一个参数都是非必传参数。
那么,我们需要对每一个参数进 if 标签来判断。
这就非常的麻烦!可能会多敲字符,导致SQL出现问题。
这个时候,就需要借助 trim 标签了。
具体怎么使用,看下面对 trim 标签的介绍和使用。

?
动态标签 - < trim >标签
之前的插??户功能,只是有?个字段可能是非必传,如果有多个字段,?般考虑使? < trim >标签 结合 < if >标签,对多个字段都采取动态?成的?式。
< trim >标签中有如下属性:
prefix:表示整个语句块,以prefix的值作为前缀
suffix:表示整个语句块,以suffix的值作为后缀
prefixOverrides:表示整个语句块要去除掉的前缀
suffixOverrides:表示整个语句块要去除掉的后缀
?
说白了,trim 就是为了方便程序员 去除 开头 /末尾 的 某个符号。
也就是说:在 开头/结尾 ,多写了这个符号,是没有问题的。
因为 trim 会将它删除掉。
如果没多写,也不会影响 程序的运行。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

下面我们来实战演练一波。

?
动态标签 - < where >标签
传?的?户对象,根据属性做where条件查询,?户对象中属性不为 null 的,都为查询条件。
如user.username 为 “a”,则查询条件为 where username=“a”:
where标签,主要作用:实现查询中的 where 替换的。
什么意思呢?
原先我们写的 where 是 SQL 的一部分,是SQL的关键字。
现在我们使用 MyBaits 提供是 where 标签来代替 SQL 中 where 关键字 。

那么,有的人可能会有疑问; 这替换跟没替换一样,还多敲 几个箭头。
因此,会产生一个问题: 使用 where 标签 的好处都有哪些?
不然不会创造出一个 where标签出来。
如果没有任何查询条件的情况下,where 标签 可以实现 隐藏 SQL 的 where 的SQL部分;但如果存在查询条件,那么会生成 where的 SQL,并且 where 标签可以自动的去除最后一个 判断条件的 and 字符。
?

也就是说:我们在拼接多个条件的时候,有几个条件涉及到非必传参数。
会有一个什么情况呢?
通常 我们拼接多个条件的时候,要么使用 or(或逻辑),要么使用 and(与逻辑)。
A and B;
当 A 条件,缺少判断的参数的时候,就会将 A 和 后面 的 and 去除掉。
这个and也就是最前面一个 and 嘛。
就变成一格判断条件的 SQL了。

下面,我们来实战一下。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

虽然比直接敲SQL,要复杂一点点。
但是,构造 SQL的灵活性,大大提高!
?
还有一个去除 and 的功能,对吧?
我们再来搞一下。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

如果 and 放在后面,就是说 and 后面没有判断条件,或者说 缺少 参数,无法构造 where SQL。会出现什么状况?
我已经帮你们测了,报错!
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

其实这个问题也很好解决。直接给你们演示个大概。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

以上标签也可以使? < trim prefix=“where” prefixOverrides=“and” > 替换。
无非就是 少用几个属性。

?
动态标签 - < set >标签
根据传?的?户对象属性来更新?户数据,可以使?标签来指定动态内容。
UserMapper 接?中修改?户?法:根据传?的?户 id 属性,修改其他不为 null 的属性:
这么说吧。set 标签,和 where 是一样。
都是替代 SQL 中的关键字。
set 就是 update 操作所需要使用的关键字。
而且,也是和 if 标签 配合使用的。
?
而且和 trim 标签一样。可以去掉最后一个符号(一般都是逗号)。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

实战演示一波。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

那如果没有 逗号呢?会是什么效果?
下面我们把三个非必传参数都传递。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

以上标签也可以使? < trim prefix=“set” suffixOverrides=“,” > 替换。

?
动态标签 - < foreach >标签
对集合进?遍历时可以使?该标签。< foreach >标签有如下属性:
collection:绑定?法参数中的集合,如 List,Set,Map或数组对象
item:遍历时的每?个对象(集合中的元素)
open:语句块开头的字符串(类似 trim的 prefix)
close:语句块结束的字符串(类似 trim的 close)
separator:每次遍历之间间隔的字符串(间隔符)
【PS:foreach 标签,是 trim 所不能替代的!】

应用场景:
通常我们要删除数据库中的信息,不是一条一套的删。
而是一删一大把。
比如:
我们要根据id来删除 记录,我们就把这些id 弄成一个结合。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

此时就可以通过 foreach 标签,进行遍历传参,删除对应的信息。
这样做的效率就很高,一次调用,就可以将需要删除的数据全部删除。
实战演示一波。
JavaEE进阶|Java进阶 - MyBatis查询数据库 && Spring Boot 单元测试 - 细节狂魔
文章图片

    推荐阅读