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

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

动态 sql 是 mybatis 的主要特性之一,在 mapper 中定义的参数传到 xml 中之后,在查询之前 mybatis 会对其进行动态解析。
mybatis 为我们提供了两种支持动态 sql 的语法:#{} 以及 ${}。

经常碰到这样的面试题目:#{}和${}的区别是什么?

网上的答案是:
#{}是预编译处理,$ {}就是简单的字符串替换。
mybatis在处理#{}时,会将sql中的#{}替换为 ? 号,调用PreparedStatement的set方法来赋值;
而mybatis在处理 $ { } 时,就是简单的字符串替换,把 ${ } 替换成变量的值作为sql的一部分。

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

我们来从代码剖析二者的区别:

${param} 传递的参数会被当成sql语句中的一部分(因此易发生sql注入),比如传递表名,字段名
例子:(传入值为id)

order by ${param} 

则解析成的sql为:

order by id


#{parm}传入的数据都当成一个字符串,会对自动传入的数据加一个双引号

例子:(传入值为id)

select * from table where name = #{param}

则解析成的sql为:

select * from table where name = "id"

为了安全,能用#的地方就用#方式传参,这样可以有效的防止sql注入攻击

什么是sql注入?
sql注入简介
直接上了百度的例子,感觉一看就清晰明了

某个网站的登录验证的SQL查询代码为:
 
strSQL = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
 
恶意填入
 
userName = "1' OR '1'='1"  与passWord = "1' OR '1'='1" 时,将导致原本的SQL字符串被填为:
 
strSQL = "SELECT * FROM users WHERE (name = '1' OR '1'='1') and (pw = '1' OR '1'='1');"
 
也就是实际上运行的SQL命令会变成下面这样的(因为sql会进行优化处理)
 
strSQL = "SELECT * FROM users;"
 
这样在后台帐号验证的时候巧妙地绕过了检验,达到无账号密码,亦可登录网站。所以SQL注入攻击被俗称为黑客的填空游戏。

动态 sql 是 mybatis 的主要特性之一,在 mapper 中定义的参数传到 xml 中之后,在查询之前 mybatis 会对其进行动态解析。
mybatis 为我们提供了两种支持动态 sql 的语法:#{} 以及 ${}。

在下面的语句中,如果 username 的值为 zhangsan,则两种方式无任何区别:

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

  其解析之后的结果均为

select * from user where name = 'zhangsan';
  
  但是 #{} 和 ${} 在预编译中的处理是不一样的。

	#{} 在预处理时,会把参数部分用一个占位符 ? 代替,变成如下的 sql 语句:

select * from user where name = ?;

  而 ${} 则只是简单的字符串替换,在动态解析阶段,该 sql 语句会被解析成

select * from user where name = 'zhangsan';

  以上,#{} 的参数替换是发生在 DBMS 中,而 ${} 则发生在动态解析过程中。

  那么,在使用过程中我们应该使用哪种方式呢?

  答案是,优先使用 #{}。因为 ${} 会导致 sql 注入的问题。看下面的例子:

对于这个题目我感觉要抓住两点:
(1)$ 符号一般用来当作占位符,常使用Linux脚本的人应该对此有更深的体会吧。既然是占位符,当然就是被用来替换的。知道了这点就能很容易区分$和#,从而不容易记错了。
(2)预编译的机制。预编译是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译。我们知道,SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作。而预编译机制则可以很好的防止SQL注入。

经过以上分析来总结一下:
总结:
可从两方面来答:

  1. #{} 和 ${} 在预编译中的处理不一样。

    - #{} 在预处理时,会把参数部分用一个占位符 ? 代替。
    - 而 ${} 则只是简单的字符串替换。		
    

    #{} 的参数替换是发生在 DBMS 中,而 ${} 则发生在动态解析过程中。

  2. ${} 传递的参数会被当成sql语句中的一部分,直接是字符串替换(因此易发生sql注入)
    #{} 传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。

那么问题来了,既然#{}比${}这么有优势为什么还要${}呢?换句话说,就是什么时候用#{},什么时候用${}呢?

什么时候用#{},什么时候用${}呢?

不知道大家有没有想过一个问题?

SELECT * FROM #{tableName};  //有没有想过??

上面我们已经说过了占位符的意义就是作为值的存在,所以如果作为值的话,那么发送的sql语句就是这样的:

SELECT * FROM  ? ;

大家都知道,带问号的sql语句是要传递参数的,好!!假如!!我们传入了参数user,那么查询语句就是这样的:

SELECT * FROM  'user' ;

请问,这种sql语句能够执行成功吗??试试就知道了!!
在这里插入图片描述
很明显语法错误!!

那么有什么办法能够解决呢??

没错!!!${}能够解决这个问题,${}的功能是直接进行字符串拼接。这也是为什么${}不能够防止一般的sql注入攻击。因为它是拼接啊!!

这样写就行了:

SELECT * FROM ${tableName} ;
//如果传入基本类型如字符串时就要把tableName改为value才能够成功取值。

因为${}是简单的字符串拼接,所以mybatis解析成了:

SELECT * FROM user ;

使用示例:
在这里插入图片描述

最后总结下:

  • 一般${}用在我们能够确定值的地方,也就是我们程序员自己赋值的地方。

  • 而#{}一般用在用户输入值的地方!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值