Mybatis(8)#{}和${}的区别,排序,模糊查询
文章目录
- [Mybatis(8)#{}和{}的区别,排序,模糊查询](#{}和{}的区别,排序,模糊查询)
- [1.#{} 和 {}](#{} 和 {})
-
- [1.1 单个参数(Integer类型):](#1.1 单个参数(Integer类型):)
- [1.2 多个参数(String)](#1.2 多个参数(String))
- [1.3 区别:](#1.3 区别:)
-
- [预编译SQL VS 即时SQL](#预编译SQL VS 即时SQL)
- 即时SQL的问题:
-
- [SQL注入 的重大安全隐患:删库](#SQL注入 的重大安全隐患:删库)
- [#{} VS {} 的区别(一道高频面试题):](#{} VS {} 的区别(一道高频面试题):)
- [2. 排序:](#2. 排序:)
- [3. 模糊查询:](#3. 模糊查询:)
-
- [3.1 使用 Mybatis 实现模糊查询:](#3.1 使用 Mybatis 实现模糊查询:)
-
- 报错原因分析:
- [将 #{} 换为 {}:](#{} 换为 {}:)
- [使用 concat 解决问题:](#使用 concat 解决问题:)
- [3.2 总结:](#3.2 总结:)
- [4. 总结](#4. 总结)
观前提醒:
如果你是第一次点击这篇博客,需要你回看Mybatis系列的这个博客:
Mybatis(7)其他查询操作(多表查询)
这篇博客的代码,都是需要自己敲一下的。
建议你看着我说的知识点,自己尝试一下,创建 Springboot项目,使用 Mybatis框架,访问数据库。
这里使用的数据库是 MySQL。
图形化工具:Navicat
sql文件获取:https://gitee.com/mrbgvhbhjv/java-ee-course

1.#{} 和 ${}
我们先使用这两种方式,在代码中,应用起来,看运行结果。
1.1 单个参数(Integer类型):
代码及运行结果:

1.2 多个参数(String)
代码及运行结果:

修正之后:

1.3 区别:
#{} :传参 ,判断类型,自动转换
${} :拼接 ,不会判断类型。
#{} :表示 预编译SQL ,使用占位符,提前预留参数位置,并根据参数类型,判断类型并转换
${} :表示 即时SQL ,拼接参数,直接执行
预编译SQL VS 即时SQL
SQL执行流程:
- 语法解析
- SQL优化
- SQL编译
- SQL执行
预编译SQL,会将前三步,缓存下来,下次使用,直接将占位符上的参数,传递,判断类型并转换就行了。
即时SQL,不会进行缓存。
所以,预编译SQL的性能 >(优于)即时SQL的性能 。
预编译SQL,参数为String时,会自动转换。
即时SQL,需要手动加 '',即 '${}'。
所以,预编译SQL的 性能和便捷性 都好于 即时SQL。
即时SQL的问题:
${} :存在 SQL注入 的重大安全隐患。
${},是直接拼接参数到 SQL 中的,SQL注入,就是利用了 参数拼接 这个机制。
SQL注入 :是通过操作 输⼊的数据 来修改 事先定义好的SQL语句 ,以达到执⾏代码对服务器进⾏攻击的⽅法。
看例子:

这就是一个典型的 SQL注入 的 SQL语句。
password='参数',语句:password = 'zhangsan123' or 1='1 ',参数是:zhangsan123' or 1='1
or 是逻辑字符,表示任意一个条件成立,结果为 true。
这条语句的意思是:查找 (username='zhangsan' 和 password = 'zhangsan123') 或者 1=1(true)
前半部分,是需要判断的,后半部分(1=1)是恒成立的(永远都是 true)。
where条件的结果就是 true。等于 where 不起作用了。
整条语句 简化之后,就是: select * from user_info
这样,就能够把所有的数据查出来了。
应用到代码上:
令 password 是 'or 1='1 。
和我上面分析的一样,这里的查询语句,会查询出所有的结果:

SQL注入 的重大安全隐患:删库
如果是这么一句 SQL语句:

password='参数',语句:password = ''; drop XXX; -- ',参数是:'; drop XXX; --
这就会产生 sql语句2,一条删除数据库的语句。
sql语句2,会把指定的数据库给删掉了!!!
一个程序,最重要的,就是程序的数据 ,数据是保存在 数据库 的。
如果执行这样的 SQL语句,删库了,这个程序就废了,数据没了。
#{} VS ${} 的区别(一道高频面试题):
#{} VS ${} 的区别:
- #{}:预编译处理, ${}:字符直接替换
- #{} 可以防⽌SQL注⼊ , ${}存在SQL注⼊的⻛险, 查询语句中, 可以使用 #{} ,推荐使用 #{}
- 但是**⼀些场景, #{} 不能完成, 比如 排序功能**, 表名, 字段名作为参数时, 这些情况需要使用${}
- 模糊查询虽然 ${}可以完成, 但因为存在SQL注⼊的问题,所以通常使用 mysql内置函数concat来完成
原则上,能使用 #{},就一定要使用 #{}。
不能使用 #{} ,要使用 ${} 的情况下,我们要考虑到 SQL注入 的问题,并处理。
2. 排序:
我们实际生活中,很多地方都需要排序,例如:医院挂号,根据病号从小到大,依次看病。
程序也是一样,购物网站/程序,总有一个功能,价格由高到低,或 由低到高。
sql语句中,我们使用两个关键字,进行排序:
- asc:升序(从小到大)
- desc:降序(从大到小)
例如,user_info 表,我要根据序号,由小到大进行排序,并查询。
从小到大,使用 asc。
sql语句:select * from user_info ORDER BY id asc ;
2.1 使用 Mybatis 实现排序功能(使用注解方式):
接口代码:
java
@Select("select * from user_info ORDER BY id #{order} ;")
List<UserInfo> selectUserInofByOrder(String order);
测试代码:
java
@Test
void selectUserInofByOrder() {
userInfoMapper.selectUserInofByOrder("asc").stream().forEach(x -> System.out.println(x));
}
运行结果(报错):

报错了。
原因分析:

原因是:asc,是 sql语句 中的关键字,Mybatis 会自动的给 asc 加 '',导致这里的 sql语句,就是这样的:
select * from user_info ORDER BY id 'asc' ;
Navicat上,执行语句,一样的报错信息了:

解决方案(待学 动态sql):
面对这种 sql语句 中的关键字,我们使用 ${} 接收,直接拼接到 sql语句,就能得出结果。
代码:
java
@Select("select * from user_info ORDER BY id ${order} ;")
List<UserInfo> selectUserInofByOrder(String order);

但是 ${} 可能会引发 sql注入 的问题,我们要处理的。
解决 sql注入 的原理就是:保证输入的参数是可控的。
但是,一般来说,desc,asc,不是用户输入的,是程序员管理的,给你两个上下箭头,选择排序规则。
内部的排序实现,是程序员处理的。
具体解决办法:学习 动态sql 才能解决。
博客链接:Mybatis(10)动态 SQL(1):新增 & 查询
3. 模糊查询:
sql语句:select * from user_info where username like '%h%'
3.1 使用 Mybatis 实现模糊查询:
接口代码:
java
@Select("select * from user_info where username like %#{username}%")
List<UserInfo> selectUserInfoByUsernameLike(String username);
测试代码:
java
@Test
void selectUserInfoByUsernameLike() {
userInfoMapper.selectUserInfoByUsernameLike("h").stream().forEach(x -> System.out.println(x));
}
运行结果:

报错原因分析:
这个代码执行之后, 实际的sql 语句 是这样的:select * from user_info where username like %'h'%
我们希望的正确 sql 是:select * from user_info where username like '%h%'
我们可以使用 ${},让 两个 % 字符,拼接到两个单引号( ' ')里面。
将 #{} 换为 ${}:
我们试试,将 #{} 换为 ${}:

运行结果:

但是 ${} 可能会引发 sql注入 的问题,我们要处理的。
解决 sql注入 的原理就是:保证输入的参数是可控的。
除了使用 ${} 解决问题之外,mysql还提供给了一个函数,用来拼接字符 的:concat函数。
使用 concat 解决问题:

sql语句:select * from user_info where username like CONCAT('%','h','%')
使用 Mybatis 实现:
接口代码:
java
@Select("select * from user_info where username like concat('%',#{username},'%') ")
List<UserInfo> selectUserInfoByUsernameLike(String username);
测试代码:
java
@Test
void selectUserInfoByUsernameLike() {
userInfoMapper.selectUserInfoByUsernameLike("h").stream().forEach(x -> System.out.println(x));
}
运行结果:

3.2 总结:
Mybatis 实现 模糊查询:
由于 like 后面的字符,无法 保证输入的参数是可控的,故而不能使用 ${},而是使用 concat。
4. 总结
这篇博客,重点掌握这个:
#{} VS ${} 的区别:
- #{}:预编译处理, ${}:字符直接替换
- #{} 可以防⽌SQL注⼊ , ${}存在SQL注⼊的⻛险, 查询语句中, 可以使用 #{} ,推荐使用 #{}
- 但是**⼀些场景, #{} 不能完成, 比如 排序功能**, 表名, 字段名作为参数时, 这些情况需要使用${}
- 模糊查询虽然 ${}可以完成, 但因为存在SQL注⼊的问题,所以通常使用 mysql内置函数concat来完成
这是一道非常高频的面试题!
关于排序语句,我们目前,只能使用 {} 进行字符的拼接,但是 {} 存在 SQL注入的问题。
所以,关于排序语句,要学习 动态sql 才能解决。
博客链接:Mybatis(10)动态 SQL(1):新增 & 查询
模糊查询,我们可以使用 MySQL提供的concat函数,进行字符的拼接。
最后,如果这篇博客能帮到你的,请你点点赞,有写错了,写的不好的,欢迎评论指出,谢谢!