最近做的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传参形式不了解,官方介绍,

MyBatis动态传递参数的两种方式#{}和${}_python

P.S. https://mybatis.org/mybatis-3/sqlmap-xml.html#Parameters

原文如下,

By default, using the #{} syntax will cause MyBatis to generate PreparedStatement properties and set the values safely against the PreparedStatement parameters (e.g. ?). While this is safer, faster and almost always preferred, sometimes you just want to directly inject an unmodified string into the SQL Statement. For example, for ORDER BY, you might use something like this:
@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);

#{}相当于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