梗概
#{}: 占位符号,可以防止sql注入,自己会带有双引号;
${}:sql拼接符号,存在sql注入问题,需要在代码中过滤以避免注入,不会带有双引号;
MyBatis排序使用order by 动态参数时,用${}而不是#{}。
详细区别
#{}:
SELECT * FROM user WHERE name = #{nameParam};
解析为一个JDBC预编译语句后:
SELECT * FROM user WHERE name = ?;
即#{} 解析为?,当 nameParam = "Bob" 时:
SELECT * FROM user WHERE name = "Bob"; //带有引号
${}:
SELECT * FROM ${tableNameParam};
${}是一个纯粹的string替换,动态解析时,会进行变量替换,不带有引号,当tableNameParam= "user" 时:
SELECT * FROM user
即预编译之前SQL中就已经不包含变量,而是被替换成常量数据了
[ sql预编译] sql 预编译指的是数据库驱动在发送 sql 语句和参数给 DBMS 之前对 sql 语句进行编译,这样 DBMS 执行 sql 时,就不需要重新编译。
详细部分小结
#{}变量的替换是在DBMS中,而${}变量的替换是在动态SQL解析阶段
使用小诀窍
优先使用#{},即能用#{}就用#{}:
1、从性能方面考虑,相同的预编译sql可以重复利用
2、${}在预编译之前就已经被替换为常量,会存在sql注入问题
表名作为变量时,必须用${}:
SELECT * FROM #{tableNameParam}
预编译完成后,sql变为:
SELECT * FROM ?
当参数tableNameParam = "user" 时,sql变为
SELECT * FROM 'user'
这会导致 sql 语法错误,表名不能加单引号 ''(反引号 ``是可以的)。
当用${}时:
SELECT * FROM ${tableNameParam}
同样当参数tableNameParam = "user" 时,sql变为:
SELECT * FROM user
同样的道理,ORDER BY排序时,也只能用${}
sql注入:
SELECT * FROM ${tableNameParam} WHERE name = #{nameParam};
假设上如sql语句,tableNameParam = user; delete user; -- 还有 nameParam = Bob,那么编译前:
SELECT * FROM user; delete user; -- WHERE name = ?;
动态编译完成后:
SELECT * FROM user; delete user; -- WHERE name = "Bob";
那么实际执行的sql一共两句:
SELECT * FROM user; delete user;
而 -- 之后的语句会被作为注释,不起作用;这时本来的一条查询语句,被注入了一个删除表数据的SQL。