一.介绍
#{} :占位符号,可以防止sql注入。替换结果会增加单引号’’
${} :sql拼接符号,替换结果不会增加单引号,like和order by 后面使用,存在sql注入问题,需手动代码过滤。
下面通过一个例子解释一下什么是sql注入。
一般开发,肯定是在前台有两个输入框,一个用户名,一个密码,会在后台里,读取前台传入的这两个参数,拼成一段SQL,例如: select count(1) from tab where usesr=userinput and pass = passinput,把这段SQL连接数据后,看这个用户名/密码是否存在,如果存在的话,就可以登陆成功了,如果不存在,就报一个登陆失败的错误。
但是有这样的情况,这段SQL是根据用户输入拼出来,如果用户故意输入可以让后台解析失败的字符串,这就是SQL注入,例如,用户在输入密码的时候,输入 ‘’’’ ’ or 1=1’’, 这样,后台的程序在解析的时候,拼成的SQL语句,可能是这样的: select count(1) from tab where user=userinput and pass=’’ or 1=1; 看这条语句,可以知道,在解析之后,用户没有输入密码,加了一个恒等的条件 1=1,这样,这段SQL执行的时候,返回的 count值肯定大于1的,如果程序的逻辑没加过多的判断,这样就能够使用用户名 userinput登陆,而不需要密码。
二:具体分析
动态sql是mybatis强大的特性之一,也是他优于其他ORM框架的原因。mybatis在对sql语句进行预编译之前,会对sql进行动态解析,解析为一个BoundSql对象,也是在此处对动态的sql进行处理的。在动态解析中,#{}和${}的表现是不一样的。
#{}:解析为一个JDBC预编译语言(prepared statement)的参数标记符。
举例:
select * from user where name = #{name};
解析后:
select * from user where name = ?;
一个#{}被解析成一个占位符。
而 ${} 只是为一个纯粹的String替换
举例:
select * from user where name = ${name};
解析后:传的值是tom
select * from user where name ="tom" ;
预编译之前的 SQL 语句已经不包含变量了,完全已经是常量数据了。
综上所得, ${ } 变量的替换阶段是在动态 SQL 解析阶段,而 #{ }变量的替换是在 DBMS 中。
三:使用
1.表名作为变量时,必须使用 ${ }
这是因为,表名是字符串,使用 sql 占位符替换字符串时会带上单引号 ‘’,这会导致 sql 语法错误,例如:
select * from #{tableName} where name = #{name};
预编译之后的sql 变为:
select * from ? where name = ?;
假设我们传入的参数为 tableName = “user” , name = “Jack”,那么在占位符进行变量替换后,sql 语句变为:
select * from 'user' where name='Jack';
上述 sql 语句是存在语法错误的,表名不能加单引号 ‘’(注意,反引号 ``是可以的)。
2.能使用 #{ } 的地方就用 #{ }
首先这是为了性能考虑的,相同的预编译 sql 可以重复利用。
其次,${ } 在预编译之前已经被变量替换了,这会存在 sql 注入问题。
四:sql预编译
1.定义:
sql预编译指的是数据驱动在发送sql语言和参数给DBMS之前对sql语句预编译,这样在DBMS执行sql的时候就不需要重新编译。
2.为什么需要预编译。
JDBC中使用对象PreparedStatement来抽象预编译语言,使用预编译。
预编译阶段可以优化sql的执行。预编译之后的sql多数情况下可以直接执行 ,DBMS不需要再次编译,越复杂的sql,编译的复杂度将增大,预编译阶段可以合并多次操作为一个操作。
预编译语言的对象可以重复利用,把一个sql预编译产生的PreparedStatement对象缓存下来,对于同一个sql,可以直接使用这个缓存的PreparedStatement对象。
mybatis默认情况下,将对所有的sql进行预编译。