Mybatis中使用 #{} 和 ${} 向sql传参时的区别

624 阅读1分钟

  今天在工作时,使用MyBatis中向sql传递两个参数时,一直显示SQL语法错误,仔细检查,才发现传入的参数被加上了引号,导致传入的参数(要传入的参数是表名)附近出现语法错误。 错误写法:

select pro_type, name, b.info 
from #{0} a inner join #{1} b 
on a.config_id = b.config_id;

这种写法在控制台报错:

select pro_type, name, b.info 
from ? a inner join ? b 
on a.config_id = b.config_id;

Cause:

  com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''dana.auto_kpi_cfg_info' a inner join 'kingnetio.meta_config_info' b on a.config' at line 1

  我们发现通过占位符传进来的参数两个表名都被加上了引号,这就导致在执行SQL时,会报语法错误。

后来特地查阅相关资料,改成使用${}:

select pro_type, name, b.info 
from ${param1} a inner join ${param2} b 
on a.config_id = b.config_id; 

控制台日志信息如下:

  Preparing: select pro_type, name, b.info from dana.auto_kpi_cfg_info a inner join kingnetio.meta_config_info b on a.config_id = b.config_id;

在传入的表名参数上没有添加引号了。 现在特此整理这两种用法的不同点:

  (1)首先一点就是,#{}传递参数时,会在传递的参数上加上引号,在传递属性比如 name=? 时,可以很方便的使用#{}。而${}则不会添加引号,传递的是什么就会直接放到SQL中去执行。

  (2)通过上面的日志信息我们可以看到,#{}传递的参数实际上是通过占位符去传入到已经预编译好的SQL中去的,所以此时的SQL已经完成编译,只需要传参数就完成执行了。而{}在日志中显示的是直接将参数拼接成完整的SQL去DBMS中编译执行的。所以#{}方式实际上比{}方式更加安全,不会引起SQL注入。但是在传入表名参数时,只能使用${},这时候,必须要在接受参数的时候加入逻辑判断,判断参数中是否存在SQL语句,以防引起注入。