Mybatis(8)#{}和${}的区别,排序,模糊查询

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. 排序:)
    • [2.1 使用 Mybatis 实现排序功能(使用注解方式):](#2.1 使用 Mybatis 实现排序功能(使用注解方式):)
  • [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执行流程:

  1. 语法解析
  2. SQL优化
  3. SQL编译
  4. 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 ${} 的区别:

  1. #{}:预编译处理, ${}:字符直接替换
  2. #{} 可以防⽌SQL注⼊${}存在SQL注⼊的⻛险, 查询语句中, 可以使用 #{} ,推荐使用 #{}
  3. 但是**⼀些场景, #{} 不能完成, 比如 排序功能**, 表名, 字段名作为参数时, 这些情况需要使用${}
  4. 模糊查询虽然 ${}可以完成, 但因为存在SQL注⼊的问题,所以通常使用 mysql内置函数concat来完成

原则上,能使用 #{},就一定要使用 #{}。

不能使用 #{} ,要使用 ${} 的情况下,我们要考虑到 SQL注入 的问题,并处理。

2. 排序:

我们实际生活中,很多地方都需要排序,例如:医院挂号,根据病号从小到大,依次看病。

程序也是一样,购物网站/程序,总有一个功能,价格由高到低,或 由低到高。

sql语句中,我们使用两个关键字,进行排序:

  1. asc:升序(从小到大)
  2. 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 ${} 的区别:

  1. #{}:预编译处理, ${}:字符直接替换
  2. #{} 可以防⽌SQL注⼊${}存在SQL注⼊的⻛险, 查询语句中, 可以使用 #{} ,推荐使用 #{}
  3. 但是**⼀些场景, #{} 不能完成, 比如 排序功能**, 表名, 字段名作为参数时, 这些情况需要使用${}
  4. 模糊查询虽然 ${}可以完成, 但因为存在SQL注⼊的问题,所以通常使用 mysql内置函数concat来完成

这是一道非常高频的面试题!

关于排序语句,我们目前,只能使用 {} 进行字符的拼接,但是 {} 存在 SQL注入的问题。

所以,关于排序语句,要学习 动态sql 才能解决。

博客链接:Mybatis(10)动态 SQL(1):新增 & 查询

模糊查询,我们可以使用 MySQL提供的concat函数,进行字符的拼接。

最后,如果这篇博客能帮到你的,请你点点赞,有写错了,写的不好的,欢迎评论指出,谢谢!

下一篇博客:Mybatis(9)数据库连接池 & 企业开发规范

相关推荐
在坚持一下我可没意见7 小时前
ideaPool论坛系统测试报告
java·spring boot·功能测试·selenium·jmeter·mybatis·压力测试
树码小子8 小时前
Mybatis(7)其他查询操作(多表查询)
spring boot·mybatis
NGC_661111 小时前
Mybatis处理流程
数据库·oracle·mybatis
SelectDB技术团队12 小时前
Apache Doris 4.0.3 版本正式发布
apache·mybatis
青云计划21 小时前
知光项目知文发布模块
java·后端·spring·mybatis
Hx_Ma161 天前
Springboot整合mybatis注解版
java·spring boot·mybatis
JavaLearnerZGQ1 天前
MyBatis的一级缓存和二级缓存
缓存·mybatis
星川皆无恙1 天前
基于Spring + SpringMVC + MyBatis图书馆管理系统设计可视化分析系统
java·大数据·数据库·后端·spring·mybatis
重生之后端学习1 天前
Maven基础
java·spring boot·spring·html·maven·mybatis