本文有点难
目录
1.一些区分
1.1参数占位符#{}和${}
1.1.1SQL注入
1.1.2like查询
1.2resultType和resultMap
2.映射查询
2.1一对一表映射
2.2一对多表映射
3.动态SQL
3.1标签
3.2标签
3.3标签
3.4标签
3.5标签
1.一些区分
1.1参数占位符#{}和${}
①什么是#{}:
这个字符实际上是预编译处理,总的来说,就是当SQL在执行的过程中,mybatis会把其#{}替换为?,而通过PrepareStatement的set来实现赋值的操作。
②什么是${}:
这个字符实际上是字符直接替换符,也就是当SQL在执行过程中,mybatis会直接把${}所在的位置进行传入参数的赋值。
③传入数值类型对两者进行验证:(1)#{}:我们可以看到其实就是进行了个预处理操作,先将id预处理(或者称为预查询)为?,然后再进行进一步的处理。
(2)${}:而这种参数的直接替换(我们也可以称其为即时查询),可能会带来越权查询和操作数据等问题,比如后面我们会讲到SQL的注入。
④传入字符串类型对两者进行验证:(1)#{}:
(2)${}:
对于其是不支持带引号的字符串类型:
如果确实需要,那么我们手动加上引号:(这个时候会发现测试用例通过),即表明是可以的 ,但是这种做法是不推荐的,后面我们会讲为什么不推荐
⑤当传入的是SQL关键字的时候:
(1)#{}:
这个时候会报错,因为关键字会被当做传入的是一个普通的值。
sql及测试代码:
输出结果:(即关键字不会被识别)
(2)${}:
不会报错,可以成功运行,这也就说明当传入的是SQL关键字的时候,我们只能用${},而不能用#{}
sql及测试代码:
输出结果:
但是!!!
值得注意的是在当我们只能使用${}的时候,我们一定要在业务代码中对传递的值来进行安全效验。下面的SQL注入就是这一点的一个体现。
1.1.1SQL注入
①什么是SQL注入:
实质上就是通过使用
${}
,使用一些特殊的语句,来达到非法获取数据的目的。②示例:(下面我们通过SQL注入来直接获得用户密码)
sql及测试代码:
结果:(能够获取到用户的密码)
结论:
因此由${}来注入很显然会引起SQL注入,这种是很不安全的,所以我们大多数时候能使用#{}就不会选择使用${},只有一些特定的SQL关键字如desc,asc这种才会使用${},如果实在迫不得已使用${}我们也要对传入的参数来进行安全校验。
1.1.2like查询
①什么是like查询?
这个问题应该很简单,就是我们之前在将sql中提到的泛查,根据一个字来查出整个用户
②注意:
这里的like查询是非常特殊的一个查询,怎么说呢?
使用#{}来进行查询会报错,而使用${}来查询在业务层的值又不能够穷举,验证非常麻烦,所以我们这里就直接示范#{}
下面我们来举个例子:
sql及测试:
结果:
显然,这出现问题是很简单的,因为#{}会被替换成一个字符串 ,而在两个%里面的是不能带有字符串的,换句话来说,该查询语句实质上就变成了,select * from userinfo where username like='%'张'%'的形式,显然,这是不行的。
③正确用法:
引入cancat,这相当于是sql中的一个字符拼接语句,它的规则如下:
concat('%',#{},'%')就相当于把三个部分拼接在了一起。
sql及测试:
结果如下:
1.2resultType和resultMap
之前我们就有说到,要是用到select进行查询的话,就必须添加属性resultType或者resultMap,那么这两者之间究竟有什么区别呢?我们下面就来谈谈。
①resultType:
(1)写法:(注意:需要写到返回类型所在的类)
(2)缺点:(当数据库名字与字段名字不同的时候,我们通过查询,就不能够获取到对应的值)举个例子:
这个时候,在数据库中表示名字的字段是username,而在userInfo这个类中,定义的字段确是name,那么这个时候,当我们进行查找的时候,就不会返回对应的name属性。
数据库:
userinfo类:
测试:
而就是为了解决这种问题,我们就引入了resultMap
②resultMap:
(1)写法:
注意:resultMap是写在Mapper标签下的,且这个标签必须写哪个属性,第一个属性是id,其它功能可以根据这个id来拿到这个 resultMap,第二个属性是type,就是写到映射对象所对应到的类。然后在这个resultMap标签下会有两个映射,一个是主键映射,主键映射的话就是<id column="" property=""></id>而在id这个标签内仍然有两个属性,column是示数据库表中的字段名,另外一个是property表示程序类中的字段名。另一个是普通映射,仍然是这两个属性来进行设置。<result column="" property=""></result>标签中的属性和主键中的含义是一致的。
(2)演示刚刚在resultType中出现的问题,是否被解决了:
改进后就会返回BaseMap这个字典:
输出结果:(字段名不一致,但是仍然可以输出)
(3)resultMap的优点:
a.字段名不一致时用来手动进行映射处理
b.可以用于多表查询
2.映射查询
2.1一对一表映射
①什么是一对一映射:
就是对于一个属性而言,它只与另一个表所在的属性有关系的映射。在这里,讲给大家演示有关于在博客系统中一个用户对应了一个博客账户的这样一一对应的关系,将在mybatis中实现这种映射关系。
②实战:
(1)我们查询articleinfo这个数据表的数据,并建立一个博客类与之对应:
建立博客类如下:
package com.example.mybatis.model; import lombok.Data; @Data public class articleinfo { private Integer id; private String title; private String content; private String createtime; private String updatetime; private Integer uid; private Integer rcount; private Integer state; //因为要与用户表实现关联,所以我们这里创建一个UserInfo的属性 private UserInfo userInfo; }
(2)创建articleinfo对应的mapper接口和xml文件:(我们以根据文章名字获取文章对象为例)
ArticleinfoMapper接口:
package com.example.mybatis.mapper; import com.example.mybatis.model.Articleinfo; import com.example.mybatis.model.UserInfo; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @Mapper public interface ArticleinfoMapper { //根据文章id获取文章标题 public Articleinfo getArticleById(@Param("id") Integer id); }
ArticleinfoMapper.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.mybatis.mapper.ArticleinfoMapper"> <select id="getArticleById" resultType="com.example.mybatis.model.Articleinfo"> select * from articleinfo where id=#{id}; </select> </mapper>
测试语句:
结果:(虽然这个时候测试的结果是没有问题的,但是我们发现userInfo=null,即没有获取到userinfo对象,除此之外,我们还可以发现这个SQL语句并不是多表联查的语句)
那么我们怎么才能得到这个对象呢?我们需要用resultMap来实现。
(3)用resultMap来实现:(不管列名的前缀来实现)
注意这个时候我们不仅需要把
a.Articleinfo
类中每个属性与数据表的映射,还要把userInfo类中的每个属性与数据相映射:
b.利用一对一结果映射标签:
<association property="这是属性名" resultMap="关联到某个mapper上" columnPrefix="列名的前缀" ></association>
(这个时候我们先不管列名的前缀来实现)
c.重写sql实现联合查询:
此时结果:(实现了联查)
(4)我们前面是没有使用列名的前缀来实现,好像也没有出现什么问题,这是因为如果两个表字段重名了,进行多表查询的时候,第一个值就会把第二的值来进行覆盖,我们下面来看看这个情况:(前缀是用来解决多表中相同字段数据覆盖的问题的)
我们修改userinfo表,将id值由1改为2
修改articileinfo中uid=2:
这个时候我们再重复刚刚的测试,我们来看具体值:(我们可以看到这个时候userinfo中的id=1,而我们刚刚明明进行修改变成了2)这就表明如果我们联系的两个表中有相同的字段,那么后一个表中的该字段会被前一个表中的该字段给覆盖。
所以我们怎么解决这个问题呢?我们列名的前缀来实现:
设置前缀就相当于把查询的结果给重命名了
使用重命名,使userinfo中的三个属性重命名为以u_开头的前缀
输出结果:
这个时候就没有被覆盖了
2.2一对多表映射
①什么是一对多映射:
一对多的关系,就是对于一个属性,它对映着多个其他的属性。举个例子,一个用户可以写多篇博客,这样就是一对多映射关系的一种体现
②与一对一映射的不同点:
(1)因为是一对多,所以我们在存储对象的时候就用list:(比如现在是一个用户,那么我们就在这个用户类中添加一个list)
(2)一对多的过程中使用的是collection标签,而里面的属性和一对一映射中都是一致的
(3)测试输出结果:
测试:
结果:
正确!!!
3.动态SQL
①什么是动态SQL:
可以理解为动态的进行调节的SQL,我举个例子,如果前端给你返回了参数,那么就直接传入返回参数的值,如果没有传入参数,那么就返回全部的值。说白了,就是用来针对非必传参数这一问题的,除此之外,其还可以解决拼接时空格的问题,以及遗漏或者多加符号的问题。
官网定义:
mybatis – MyBatis 3 | 动态 SQL
②动态SQL的体现:
我们这里的动态SQL主要就从以下5个标签中来进行讲解。
3.1<if>标签
①作用:
用来判断一个参数是否有值,要是有值就返回里面的值,要是没有,就隐藏if中的sql
②格式:
<if test="表达式">
sql
</if>
举个实例:
<if test="username!=null">
username=#{username}
</if>
结果,如果为真,那么原SQL语句就会直接将其拼接上;如果为假,那么if中的sql就会被忽略。
③实战演练:
(1)不添加photo参数,直接忽略if标签内容:
sql及测试:
结果显示:
(2)添加photo的参数情况,拼接标签内容:
sql及测试:
结果显示:
3.2<trim>标签
①作用:(其可以与其他标签进行搭配使用)
最主要的就是去掉SQL语句前后某个多余的字符。这里面有四个参数:
prefix:表示整个语句块,以prefix的值作为前缀
suffix:表示整个语句块,以suffix的值作为后缀
prefixOverrides:表示整个语句块要去除掉的前缀
suffixOverrides:表示整个语句块要去除掉的后缀
②格式:
<trim prefix="前缀符", suffix="后缀符", prefixOverrides="去除多余的前缀字符", suffixOverrides="去除多余的后缀字符"> <if test="表达式"> ... </if> ... </trim>
③实战演练:
(1)不添加photo内容情况:
sql及测试:
输出结果:
(2)添加所有的情况:
sql及测试:
结果显示:
3.3<where>标签
①作用:
替代SQL语句中的where标签,如果满足即执行,不满足就忽略,同时它还可以自动去除最前面的and字符
②格式:
<where>
.....
</where>
③实战例子:
(1)输入某个id来进行查询该用户信息:
sql及测试:(执行到了where中的if字句)
结果如下:
(2)不输入id看是否能查询到用户信息:(即输入为null)
sql及测试:
结果如下:测试是失败了,但是却找到了所有的user
(3)测试去除最前面的and字符
sql及测试:
结果如下:(and被自动去除掉了)
3.4<set>标签
①作用:
就是在修改sql所执行的操作,配合if来进行非必要输入的,同时它还可以自动去除最后一个英文逗号
②格式:
<set>
...
</set>
③实战示例:
(1)使用set标签来执行SQL:
sql及测试:
结果:
数据库查看:
(2)实现自动去除最后一个英文逗号:
sql及测试:
结果:
3.5<foreach>标签
①作用:
是对集合进行循环使用的,常用于删除操作
collection:绑定方法参数中的集合,如 List,Set,Map或数组对象
item:遍历时的每一个对象
open:语句块开头的字符串
close:语句块结束的字符串
separator:每次遍历之间间隔的字符串
这里的open和close就有点类似于我们前面trim中prefix和suffix的作用
②格式:
<foreach collection="集合名" open=" " close=" " separator=" " item=" ">
...
</foreach>
③实战示例:
删除前的数据表:
sql及测试:(删除id为678的用户)
结果:
并查看数据库: