MyBatis中 #{} 和 ${} 区别
文章目录
- 1. 使用场景
- 2. `#{}` 和 `${}` 的用法和区别
- 2.1 区别一
- 2.2 区别二
- 什么是 SQL 注入?
- 为什么 `#{}` 可以防止 **SQL 注入**?
- 3. 只能使用 `${}` 的场景
- 4. 总结
1. 使用场景
动态 SQL:根据不同的条件拼接出不同的 SQL 语句。这样的 SQL 更灵活,也能适用更多的场景。
在 Mybatis 的 Mapper.xml 映射文件中编写 SQL 语句时,#{}
和 ${}
就是用来对引入的形参变量进行取值的。#{}
和 ${}
都可以用来对变量取值,但用法却有不同,需要格外注意。
2. #{}
和 ${}
的用法和区别
2.1 区别一
#{}
:在解析变量时,会将变量的值取出,并自动给其添加引号,然后拼接到 SQL 语句中;${}
:在解析变量时,直接将变量的值取出,然后直接拼接到 SQL 语句中。
假设,我要写一个 select 语句,定义了一个变量 name = “Pual”。当我用 #{}
拼接 SQL 时
<select id="queryByName" parameterType="String" resultType="HashMap">
SELECT * FROM user WHERE username=#{value}
</select>
我们得到拼接的 SQL 为:
SELECT * FROM user WHERE username="Pual";
当我用 #{}
拼接 SQL 时,
<select id="queryByName" parameterType="String" resultType="HashMap">
SELECT * FROM user WHERE username=${value}
</select>
我们得到拼接的 SQL 为:
SELECT * FROM user WHERE username=Pual;
这条语句在执行的时候会报错。可见,这种情况下我们是不能使用 ${}
拼接的。
2.2 区别二
${}
:直接将变量值和 SQL 拼接成完整的 SQL 语句后,再进行编译。#{}
:先用占位符代替参数将SQL语句先进行预编译,然后再将变量值替换进来。
因此,#{}
方式可以防止 SQL 注入问题。下面我们分析原因。
什么是 SQL 注入?
在使用 SQL 语句进行数据库操作时,如果对入参没有校验,那么攻击者就可以构造特殊的 SQL 语句,直接获取或修改数据库中的数据。
例如,我们有一个需求,给定了一个用户的姓名和密码,要求从用户表(user)中查询出该用户的信息。如果我们使用 ${}
的方式实现,即
<select id="queryByName" parameterType="String" resultType="HashMap">
SELECT * FROM user WHERE username='${username}' and password='${password}'
</select>
我们正常传入一组数据:username=“pual”、password=“123456” 当然不会有问题。假如我是一个恶意攻击者,我可以构造一组参数:username=“pual’ or 1=1 #”,密码随意输一个"123",我们会得到
SELECT * FROM user WHERE username='pual' or 1=1 # and password='${password}'
这样,# 会将它后面的 SQL 注释掉,而 # 前面的 where 条件一直为 true,这样我就可以获取到所有用户的信息。
为什么 #{}
可以防止 SQL 注入?
#{}
方式先用占位符代替参数,对 SQL 语句进行预编译后,再将参数中的内容替换到 SQL 中。由于 SQL 语句已经被预编译过,参数中的内容,无法变为 SQL 命令的一部分,攻击者也就无法再通过非法的参数去修改 SQL 命令了。
3. 只能使用 ${}
的场景
根据上面的例子,我们能看出,#{}
会给引用的变量加上引号,所以当我们的变量中要传入的是表名或者表的字段名时,这种情况下是不能使用 #{}
的,只能使用 ${}
。例如
<select id="queryByName" parameterType="String" resultType="HashMap">
SELECT * FROM ${tableName} WHERE username="Pual"
</select>
4. 总结
- 当我们的变量中要传入的是表名或者表的字段名时,只能使用
${}
。其余场景下虽然#{}
和${}
都适用,但更建议使用#{}
,以防止 SQL 注入。 #{}
方式会进行预编译,参数是在编译之后填充进去的,所以不需要加上引号;${}
方式实现的是 SQL 语句的直接拼接,如果是需要添加引号的场景,需要子级手动在 SQL 中添加引号。#{}
能预防 SQL 注入,${}
不能。