java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Mybatis #{}和${}的区别

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

作者:蜀州凯哥

${} 和 #{} 都是 MyBatis 中用来替换参数的,它们都可以将用户传递过来的参数,替换到 MyBatis 最终生成的 SQL 中,但它们区别却是很大的,今天通过本文介绍下MyBatis中${} 和 #{} 有什么区别,感兴趣的朋友跟随小编一起看看吧

${} 和 #{} 都是 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注入,提高系统的安全性

到此这篇关于MyBatis中${} 和 #{} 有什么区别()的文章就介绍到这了,更多相关Mybatis #{}和${}的区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文