最近做的Java规范更新涉及到MyBatis映射配置文件中动态传递参数的两种方式#{}和${},两者的区别,
(1) #{}为参数占位符?,即SQL预编译。${}为字符串替换,即SQL拼接,可以理解为仅仅是个纯碎的string替换,在动态SQL解析阶段将会进行变量替换。
(2) #{}是“动态解析->预编译->执行”的过程。${}是“动态解析->编译->执行”的过程。
(3) #{}的变量替换是在DBMS中。${}的变量替换是在DBMS外。
(4) 变量替换后,#{}对应的变量自动加上引号。变量替换后,${}对应的变量不会加上引号。
例如给参数name传递一个值test,如果是#{name},则值为'test',
select id,name,age from student where name=#{name}
如果是${name},则值为test,
select id,name,age from student where name=${name}
(5) #{}能防止SQL注入。${}不能防止SQL注入。
默认情况下,使用#{}格式的语法会导致MyBatis创建预处理语句属性并以他为背景设置安全的值(例如?)。这样做很安全,很迅速,是首选做法,有时只是想直接在SQL语句中插入一个不改变的字符串。例如ORDER BY,可以这样来使用ORDER BY ${columnName},这里MyBatis不会修改或转义字符串。
但是要知道,接受从用户输出的内容并提供给语句中不变的字符串,这样做是不安全的。这会导致潜在的SQL注入攻击,因此不应该允许用户输入这些字段,或者通常自行转义并检查。
ORDER BY通常比较特殊,例如根据前端传过来的字段排序,用了如下格式,
select XXXX from table order by #{column} #{desc}
但是排序没生效,查看日志,发现实际执行的SQL如下所示,排序未生效,
select XXXXX from table order by "column" "desc"
主要还是对MyBatis传参形式不了解,官方介绍,

P.S. https://mybatis.org/mybatis-3/sqlmap-xml.html#Parameters
原文如下,
#{}相当于jdbc中的preparedstatement,进行了预编译,而${}直接是字符串本身,是有意设计成这样,方便拼接成动态SQL,但可能存在注入的问题。
另外一个场景,就是隐式转换,SQL Server碰到过传入的是varchar,字段类型是varchar,但是通过#{},传入的就成了nvarchar,例如,
select * from test where id = #{id};
导致隐式转换,此时有两种解决,
(1) 需要在jdbc的url配置中添加sendStringParameterAsUnicode=false;关闭unicode字符串的转换,
jdbc:sqlserver://x.x.x.x:1433;DatabaseName=test;sendStringParametersAsUnicode=false;
(2) #{}改为${},避免类型转换,
select * from test where id = '${id}';
因此,
(1) 能用#{}的地方就用#{},不用或少用${}。
(2) 表名作参数时,必须用${},例如select * from ${tableName}。
(3) ORDER BY时,必须用${},例如,
select * from t_user order by ${columnName}
(4) 使用${}时,要注意何时加或不加单引号,即${}和'${}'。
(5) 存在隐式转换时,注意${}和#{}。
参考资料,
https://mybatis.org/mybatis-3/sqlmap-xml.html#Parameters
https://www.cnblogs.com/yang82/p/7813549.html
https://blog.csdn.net/siwuxie095/article/details/79190856
https://blog.csdn.net/angyuhh07719/article/details/102429418
近期更新的文章:
《知乎的彩蛋》
文章分类和索引:




