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

 更新时间:2022年11月30日 11:23:30   作者:蜀州凯哥  
${} 和 #{} 都是 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 #{}和${}的区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java控制台实现聊天程序

    java控制台实现聊天程序

    这篇文章主要为大家详细介绍了java控制台实现聊天程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • Java 和 JavaScript 真正通用的Base64编码详解

    Java 和 JavaScript 真正通用的Base64编码详解

    这篇文章主要介绍了Java 和 JavaScript 真正通用的Base64编码详解的相关资料,附有Java和Javascript 的Base64编码,需要的朋友可以参考下
    2016-11-11
  • JPA之EntityManager踩坑及解决:更改PersistenceContext

    JPA之EntityManager踩坑及解决:更改PersistenceContext

    这篇文章主要介绍了JPA之EntityManager踩坑及解决:更改PersistenceContext方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • mybatisplus之Wrappers.ne踩坑记录解决

    mybatisplus之Wrappers.ne踩坑记录解决

    这篇文章主要为大家介绍了mybatisplus之Wrappers.ne踩坑记录解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • Maven项目中pom.xml父子模块配置小结

    Maven项目中pom.xml父子模块配置小结

    Maven父子模块结构通过pom.xml实现配置复用、版本统一和批量构建,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-08-08
  • Java实现简易HashMap功能详解

    Java实现简易HashMap功能详解

    这篇文章主要介绍了Java实现简易HashMap功能,结合实例形式详细分析了Java实现HashMap功能相关原理、操作步骤与注意事项,需要的朋友可以参考下
    2020-05-05
  • SpringBoot如何整合redis实现过期key监听事件

    SpringBoot如何整合redis实现过期key监听事件

    这篇文章主要介绍了SpringBoot如何整合redis实现过期key监听事件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • 借助Maven搭建Hadoop开发环境的最详细教程分享

    借助Maven搭建Hadoop开发环境的最详细教程分享

    在Maven插件的帮助下,VSCode写Java其实非常方便,所以本文就来和大家详细讲讲如何借助maven用VScode搭建Hadoop开发环境,需要的可以参考下
    2023-05-05
  • 解决在IDEA下使用JUnit的问题(解决过程)

    解决在IDEA下使用JUnit的问题(解决过程)

    很多朋友跟小编反馈在IDEA下使用JUnit进行实例测试的时候出现很多奇葩问题,今天小编通过本文给大家分享idea使用JUnit出现问题及解决过程,感兴趣的朋友跟随小编一起看看吧
    2021-05-05
  • 阿里面试Nacos配置中心交互模型是push还是pull原理解析

    阿里面试Nacos配置中心交互模型是push还是pull原理解析

    这篇文章主要为大家介绍了阿里面试Nacos配置中心交互模型是push还是pull原理解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07

最新评论