1>.#{}
是预编译处理,${}
是字符串替换;
2>.在动态SQL解析阶段,#{}
和${}
会有不同的表现:
例如SQL语句: select * from table where field1=#{xxx} AND field2=‘${xxx}’;
①.
#{}
会被解析为一个JDBC预编译语句(prepared statement)的参数标记符占位符?
,之后再调用PreparedStatement的set方法来赋值.上面的SQL语句就被解析为:...... field1=?
②.
${}
仅仅为一个纯碎的字符串替换,在mybatis的动态SQL解析阶段将会进行变量替换
.比如:传入的field2参数是’张三’.上面的SQL语句就被解析为:...... field2='张三'
解析完成的完整语句就是:
select * from table where field1=? AND field2='张三'
最后把解析好的SQL语句再通过JDBC执行;
3>.使用#{}
可以有效的防止SQL注入
,提高系统安全性.原因在于:预编译机制.预编译完成之后,SQL的结构已经固定,即便用户输入非法参数,也不会对SQL的结构产生影响,从而避免了潜在的安全风险;
①.预编译是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译.我们知道,SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作.而预编译机制则可以很好的防止SQL注入;
②.在动态SQL解析阶段’#{}‘部分会被解析为一个JDBC预编译语句(prepared statement)的参数标记占位符’?'.例如
select * from tableName where field = ?
,而传入的参数将会经过PreparedStatement方法的强制类型检查和安全检查等处理,最后作为一个合法的字符串传入;例如传入参数为
a'or'1=1
,使用#{}
,经过SQL动态解析和预编译,会把单引号''
转义为\'
,那么SQL最终解析为:select * from table where field = "a\' or \'1=1"
;
4>.再看${}
这种方式只会做简单的字符串替换,在动态SQL解析阶段将会进行变量替换.
①.假如传递的参数
为51mn
,SQL最终解析为:select * from table where field = '51mn'
;
②.由于参数部分在预编译处理之前就已经被替换,传入的参数没有经过PreparedStatement方法的强制类型检查和安全检查等处理,会导致SQL语句有被注入的风险;
例如传入的参数是
a'or'1=1
,那么使用${}
经过处理后直接替换字符串的SQL就解析为:select * from table where field = 'a' or '1=1'
,那么最终的结果肯定不是用户想要的!