亲宝软件园·资讯

展开

MyBatis中${} 和 #{} 有什么区别小结

蜀州凯哥 人气:0

${} 和 #{} 都是 MyBatis 中用来替换参数的,它们都可以将用户传递过来的参数,替换到 MyBatis 最终生成的 SQL 中,但它们区别却是很大的,接下来我们一起来看。

1.功能不同

${} 是将参数直接替换到 SQL 中,比如以下代码:

9b59850a93fd4eb08c1df0b641b36bb6.png

最终生成的执行 SQL 如下: 

5224c1bee05d4556973b555f522c49df.png

从上图可以看出,之前的参数 ${id} 被直接替换成具体的参数值 1 了。 而 #{} 则是使用占位符的方式,用预处理的方式来执行业务,我们将上面的案例改造为 #{} 的形式,实现代码如下: 

8117f33d8b9d4ab4a61be660d9282755.png

最终生成的 SQL 如下: 

6e39a73a21f84dbb9a9dfeab749ad273.png

${} 的问题

当参数为数值类型时(在不考虑安全问题的前提下),${} 和 #{} 的执行效果都是一样的,然而当参数的类型为字符时,再使用 ${} 就有问题了,如下代码所示:

73fef915683a4aa8a32e743a7bb6b0cf.png

以上程序执行时,生成的 SQL 语句如下: 

1213351fdb6b41a18ce3f6710f7275fc.png

这样就会导致程序报错,因为传递的参数是字符类型的,而在 SQL 的语法中,如果是字符类型需要给值添加单引号,否则就会报错,而 ${} 是直接替换,不会自动添加单引号,所以执行就报错了。 而使用 #{} 采用的是占位符预执行的,所以不存在任何问题,它的实现代码如下: 

31b491eeb9d24395884224f861f14825.png

以上程序最终生成的执行 SQL 如下: 

bd729752a5a64f4fa7e348a8b63fd861.png

2.使用场景不同

虽然使用 #{} 的方式可以处理任意类型的参数,然而当传递的参数是一个 SQL 命令或 SQL 关键字时 #{} 就会出问题了。比如,当我们要根据价格从高到低(倒序)、或从低到高(正序)查询时,如下图所示:

69ae137770bf4b0cb027b7edeb15fc53.png

此时我们要传递的排序的关键字,desc 倒序(价格从高到低)或者是 asc 正序(价格从低到高),此时我们使用 ${} 的实现代码瑞安: 

9c7b6d44237e469cbf47e527ddac67d1.png

以上代码生成的执行 SQL 和运行结果如下: 

a04b8c376d84473fb1ae0916b99b7d10.png

但是,如果将代码中的 ${} 改为 #{},那么程序执行就会报错,#{} 的实现代码如下: 

b7badf6a5b3a4eaca7f05a38b068f6fd.png

以上代码生成的执行 SQL 和运行结果如下: 

7e19dac52d7c4483b799e5b424cf9984.png

从上述的执行结果我们可以看出:当传递的是普通参数时,需要使用 #{} 的方式,而当传递的是 SQL 命令或 SQL 关键字时,需要使用 ${} 来对 SQL 中的参数进行直接替换并执行。 

3.安全性不同

${} 和 #{} 最主要的区别体现在安全方面,当使用 ${} 会出现安全问题,也就是 SQL 注入的问题,而使用 #{} 因为是预处理的,所以不会存在安全问题,我们通过下面的登录功能来观察一下二者的区别。

3.1 使用 ${} 实现用户登录

UserMapper.xml 中的实现代码如下:

ee4d60465c1c44ce8430323707ce46f2.png

单元测试代码如下: 

cf152dc228df444ebbd4d06dfd1e5801.png

以上代码生成的执行 SQL 和运行结果如下: 

fbd71fbe2c864a3da766a54f4f78e7dd.png

从结果可以看出,当我们传入了正确的用户名和密码时,能成功的查询到数据。但是,在我们使用 ${} 时,当我们在不知道正确密码的情况下,使用 SQL 注入语句也能用户的私人信息,SQL 注入的实现代码如下: 

d305be09f7f046ebbbf8122dc07483ef.png

以上代码生成的执行 SQL 和运行结果如下: 

edbb79eb1ff34cca8806e18d477999de.png

从上述结果可以看出,当使用 ${} 时,在不知道正确密码的情况下也能得到用户的私人数据,这就像一个小偷在没有你们家钥匙的情况下,也能轻松的打开你们家大门一样,这是何其恐怖的事情。那使用 #{} 有没有安全问题呢?接下来我们来测试一下。 

3.2 使用 #{} 实现用户登录

首先将 UserMapper.xml 中的代码改成以下内容:

1067f38ca16147958d48b0e16664b2f0.png

接着我们使用上面的 SQL 注入来测试登录功能: 

821acd85b7ca433a825b1884892a91d7.png

最终生成的 SQL 和执行结果如下: 

6149159c624a47c4af6f75b9b0bd1e32.png

从上述代码可以看出,使用 SQL 注入是无法攻破 #{} 的“大门”的,所以可以放心使用。 

总结

${} 和 #{} 都是 MyBatis 中用来替换参数的,它们二者的区别主要体现在:1、功能不同:${} 是直接替换,而 #{} 是预处理;2、使用场景不同:普通参数使用 #{},如果传递的是 SQL 命令或 SQL 关键字,需要使用 ${},但在使用前一定要做好安全验证;3、安全性不同:使用 ${} 存在安全问题,而 #{} 则不存在安全问题。

补充知识点:

Mybatis——#{}和${}的区别

在使用mybatis的时候我们会使用到#{}和${}这两个符号来为sql语句传参数,那么这两者有什么区别呢?

#{}是预编译处理,是占位符,${}是字符串替换,是拼接符

Mybatis在处理#{}的时候会将sql中的#{}替换成?号,调用PreparedStatement来赋值
如:

select * from user where name = #{userName};

设userName=yuze

看日志我们可以看到解析时将#{userName}替换成了

select * from user where name = ?;

然后再把yuze放进去,外面加上单引号

Mybatis在处理 的 时 候 就 是 把 {}的时候就是把 的时候就是把{}替换成变量的值,调用Statement来赋值
如:

select * from user where name = #{userName};

设userName=yuze

看日志可以发现就是直接把值拼接上去了

select * from user where name = yuze;

这极有可能发生sql注入,下面举了一个简单的sql注入案例
这是一条用户的账号、密码数据

在这里插入图片描述

当用户登录,我们验证账号密码是否正确时用这个sql:

username=yyy;password=123

select * from user where username=${username} and password=${password}

显然这条sql没问题可以查出来,但是如果有人不知道密码但是想登录账号怎么办

我们不需要填写正确的密码:

密码输入1 or 1=1,sql执行的其实是

select * from user where username='yyy' and password=1 or 1 =1

#{}的变量替换是在DBMS中、变量替换后,#{}对应的变量自动加上单引号

的 变 量 替 换 是 在 D B M S 外 、 变 量 替 换 后 , {}的变量替换是在DBMS外、变量替换后, 的变量替换是在DBMS外、变量替换后,{}对应的变量不会加上单引号

使用#{}可以有效的防止sql注入,提高系统的安全性

加载全部内容

相关教程
猜你喜欢
用户评论