目录
上篇文章中,对 MyBatis 操作数据库的基本知识,我们有了一些了解。
这篇文章,由增删改查中的 “查” 引出。
当我们执行 SQL 语句:select * from user_info
返回的结果中,有一些属性并未成功获取到值

进行观察,发现,如果数据库字段和 Java 对象属性一致,就会自动赋值,如果数据库字段中,出现下划线,而 Java 对象属性中用驼峰转换了,就会出现无法赋值的情况:

解决数据库字段和Java对象属性不同
解决办法:
1. 起别名
2. 结果映射
3. 开启驼峰命名
起别名
在 SQL 语句中,给列名起别名,保持别名和实体类属性名一样

结果映射

如果其他 SQL 语句,也希望可以复用这个映射关系,则可以给这个 Results 定义一个名称。
使用 Rsults 中的 id 属性来给 Results 定义别名,再使用 @ResultMap 注解来复用其他定义的 ResultMap

开启驼峰命名
我们可以直接在配置信息中配置,自动按照规范,将数据库使用的蛇形命名法,转换为 Java 属性规范遵循的驼峰命名法~~

然后 Java 代码中不需要做任何处理,就可以正常获得值了

MyBatis XML 配置文件
上面学习了注解的方式来使用 MyBatis 进行开发,还可以使用 XML 的方式来对 MyBatis 来进行开发。
配置连接字符串和 MyBatis

写持久层代码
1. 方法定义 Interface
2. 方法实现:XXX.xml

添加 mapper 接口

添加 UserInfoXmlMapper.xml
因为我们在配置文件中配置了路径,在 resources 下的 mapper 中的 **Mapper.xml

所以需要在 resources 下创建 mapper 文件夹,在 mapper 文件夹,创建名为 **Mapper.xml,** 表示多位模糊匹配~

UserInfoXmlMapper.xml 中,有 MyBatis 的固定 xml 格式:

在 <mapper> 双标签中实现查询所有用户的具体实现。

标签说明:
<mapper>:需要指定 namespace 属性,值为 mapper 接口的全限定名。
<select> 查询标签:用来执行数据库查询操作
id:是和 Interface 中定义的方法名称一样的,表示对接口的具体方法实现。
resultType:表示返回的数据类型,也就是我们最初在 model 包中定义的实体类~

进行单元测试发现符合预期~
增删改查操作
接下来,我们使用 XML 形式的 MyBatis 来实现用户的增删改查操作
增(Insert)


删(Delete)

改(Update)

查(Select)
在 xml 中,同样也存在数据封装问题。当数据库字段和 Java 对象属性不一致的时候,无法直接赋值。
解决办法和注解类似:
起别名,结果映射,开启驼峰命名
起别名就是在 sql 语句中进行操作即可,这里不做详解。
开启驼峰命名更加简单,在配置文件中配置即可,这里也不做详解。
我们学习在 xml 中写结果映射
注意,使用结果映射,select 标签中的属性为 resultMap,如果不使用结果映射,属性应该是 resultType


多表查询
再创建一张文章表,进行多表关联查询
-- 创建文章表
DROP TABLE IF EXISTS articleinfo;
CREATE TABLE articleinfo (
id INT PRIMARY KEY auto_increment,
title VARCHAR ( 100 ) NOT NULL,
content TEXT NOT NULL,
uid INT NOT NULL,
delete_flag TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常,1-删除',
create_time DATETIME DEFAULT now(),
update_time DATETIME DEFAULT now()
) DEFAULT charset 'utf8mb4';
-- 插入测试数据
INSERT INTO articleinfo ( title, content, uid ) VALUES ( 'Java', 'Java正文', 1 );
对应 model:

sql 语句:

对 sql 语句的解释:

因为查询的语句涉及到了 UserInfo 中的属性,进行 model 补充:

接口定义:

#{} 和 ${}
Integer 类型的参数

我们输入的参数,并没有在后面拼接,id 的值使用 ?来进行占位。这种 SQL 我们称之为 “预编译 SQL”
把 #{ } 修改为 ${ } 再观察日志:


可以看到,我们的参数直接拼接在 SQL 语句中了
String 类型的参数
使用 #{} 接收参数

使用 ${} 接收参数

可以通过日志看到,此时的参数,是直接拼接到 SQL 语句中了,但是字符串作为参数的时候,需要添加引号 ‘ ’,当我们使用 ${} 的时候,不拼接 ‘ ’ ,会导致程序发生报错。
我们可以手动在 ${} 中添加引号

可以观察:
#{ } 使用的是预编译 SQL,通过 ? 占位的方式,提前对 SQL 语句进行了编译,将参数直接填充到 SQL 语句中,#{ } 会根据参数类型,自动拼接引号 ‘ ’
${ } 会直接字符替换,一起对 SQL 语句进行编译,如果参数为字符串,需要加上引号~
区别
两者一个是预编译 SQL,一个是即时 SQL。
当客户发送一条 SQL 语句给服务器后,大致会有如下流程:
1. 解析语法语义,校验 SQL 语句是否正确
2. 优化 SQL 语句,制定执行计划
3. 执行并返回结果
如果一条 SQL 语句经过如上流程的处理,我们就称之为 即时 SQL
预编译 SQL 语句的好处:
1. 性能更高
大部分情况下,某一条 SQL 语句可能会被重复的反复调用,或者每次执行的时候,只有个别的值不同(例如:select 中的 where 的 id 不同,update 的 set 字句不同...)如果每次都经过上面三个流程,进行语法语义解析,优化语句,则会降低效率。

预编译 SQL 语句,编译一次之后,会将编译后的 SQL 语句缓存起来,当后面再次执行这条语句的时候(如果只是参数不同),不会再进行编译,省去了解析优化过程,提高效率。
2. 更安全(防止 SQL 注入)
SQL 注入:在学习 JDBC 的时候,我们有所了解,是通过操作输入的数据,来将预先定义好的 SQL 语句进行修改,以达到执行代码对服务器进行攻击的方法。
sql 注入代码: ' or 1 = ' 1
正常情况:


SQL 注入:

排序功能
上面的区别中,我们将了 #{} 的好处,即也是 ${} 的缺点,会有 SQL 注入的风险。所以绝大部分情况下,我们尽量使用 #{} 完成 SQL 语句
但 ${} 存在,即有其合理性。
当我们要操作数据库,完成对数据的排序功能:

这时候,就需要使用 ${ } 来实现排序查询了,如果使用 #{ } 会当作参数,无法实现。


like 模糊查询
SQL 语句使用 like 进行模糊查询的时候,使用 #{} 会报错


可以将 #{} 改为 ${} 查询,但 ${} 存在 SQL 注入的问题,所以还不能直接使用 ${} 。
我们可以使用 mysql 的内置函数 concat()来进行处理。

数据库连接池
在 MyBatis 中,我们使用了数据库连接池的技术,避免了频繁的创建,销毁连接造成的性能消耗。
数据库连接池,分则分配,管理,释放数据库连接,允许程序对某一个现有的数据库来重复使用,而不是多次新建数据库连接。

使用数据库连接池的情况:程序启动时,会在数据库连接池中创建一定数量的 Connection 对象,当客户请求数据库连接池,会从数据库连接池中获取 Connecttion 对象,然后执行 SQL,SQL 语句执行完毕之后,再把 Connection 对象归还给数据库连接池,
使用
常见的数据库连接池:C3P0 DBCP Druid Hikari
SpringBoot 默认使用的是Hikari 数据库连接池

如果想把默认的数据库连接池切换为 Druid 数据库连接池,只需要引入相关依赖即可~

运行结果如下

总结
MySQL 规范
1. 表名,字段使用小写字母或者数字,单词之间以下划线分割。不允许出现数字开头或者两个下划线中间只出现数字的情况。
2. 建表必备三字段:id,create_time,update_time
3. 在表查询的时候,大部分情况,不使用 * 作为查询的字段列表。
#{} 和 ${} 区别
1. #{} 预编译处理 ${} 字符直接替换
2. #{} 可以防止 SQL 注入,${} 存在 SQL 注入的风险
3. 一些情况下,必须使用 ${} 例如:排序功能,表名,字段名作为参数,需要使用 ${},但一定要考虑到 SQL 注入的风险,并提前进行规避。
4. 模糊查询虽然 ${} 可以完成,但存在 SQL 注入的风险,通常使用 mysql 的内置函数 concat 来完成。
动态 SQL
Mybatis 最强大特性之一 -- 动态 SQL,能够完成不同条件下的 SQL 拼接。
if 标签
举例:当用户在注册页面,可能出现如下情况:

注册表格中分为两种:必填和非必填字段,如果在添加用户的时候有不确定的字段传入,程序该如何实现呢???
比如:添加 gender 为非必填字段,具体实现如下:

Mapper.xml 实现:

trim 标签
上面的插入用户功能,只有一个 gender 字段可能是选填项。但如果有多个字段都是选填项,我们考虑使用标签结合标签,对多个字段都同时采用动态生成的方式。
如果继续单纯的使用 <if> 会导致最后拼接完的 sql 语句多出一个逗号,所以需要结合 <trim> 标签
<trim> 标签中有如下属性:
prefix:表示整个语句块,以 prefix 的值作为前缀。
suffix:表示整个语句块,以 suffix 的值作为后缀。
prefixOverrides:表示整个语句块要去除的前缀。
suffixOverrides:表示整个语句块要去除的后缀,
调整代码为:


where 标签
有如下需求:

系统需要根据用户的筛选条件,动态的组装 where 条件
需求:传入用户对象,根据属性做 where 条件查询。用户对象中属性不为 null 的,都为查询条件。
接口定义:

Mapper.xml 实现:

<where> 只会在子元素有内容的情况下,插入 where 字句,而且会自动去除字句的开头的 and 或 or。以上标签页可以使用 <trim prefix = "where" prefixOverrides = "and"> 代替,但是当子元素没有内容的时候,where 关键字仍然会保留。
set 标签
需求:根据传入的用户对象属性来更新用户数据。
接口定义:根据传入的用户 id 属性,修改其他不为 null 的属性。

Mapper.xml:

<set>:会动态的在 SQL 语句中添加 set 关键字,并删除额外的逗号。
补充:Java 属性,数据库字 与 Java 代码对应

foreach 标签
对集合进行遍历的时候,可以使用该标签。标签有如下属性:
collection:绑定方法参数中的集合,如 List,Set,Map 或 数组对象
item:遍历时的每一个对象
open:语句块开头的字符串
close:语句块结束的字符串
separator:每次遍历之间间隔的字符串
需求:根据多个 userId,删除用户的数据
接口方法:

Mapper.xml 中增加 SQL 语句

include 标签
在 xml 映射文件中,配置的 SQL 语句,会出现很多重复的片段,较多的冗余代码。

我们可以对重复的代码片段进行抽取,将其通过 <sql> 标签封装到一个 SQL 片段,然后再通过 <include> 标签进行引用。
<sql>:定义可重用的 SQL 片段
<include>:通过属性 refid,指定包含的 SQL 片段







