动态sql是mybatis的主要特性之一。在mapper中定义的参数传到xml中之后,在查询之前mybatis会对其进行动态解析。
mybatis提供了两种支持动态sql的语法:#{} 和 ${}。
select * from t_user where username = '${username}' and password='${password}';
select * from t_user where username = #{username} and password=#{password};
如果传值是一样的话,#{} 和 ${}的运行结果是一样的,但是在SQL解析阶段的处理是不一样的
1、#{}
解析为一个JDBC预编译语句(prepared statement)的参数标记符,把参数部分用占位符 ? 代替。
动态解析为:
select * from t_user where username = ? and password = ?;
就相当于JDBC 的prepared statement
2、${}
这种方式只会做简单的字符串替换,在动态SQL解析阶段将会进行变量替换,假如传递的参数为二师兄,最后相当于:
select * from t_user where username = '二师兄' and password='123456';
这样在预编译之前的sql语句已经不包含变量了,因此可以看出 ${} 变量的替换阶段是在动态SQL解析阶段。
#{} 和 ${}比较 可以有效的预防SQL注入
举个例子:
如果传入的username为张三’# password为 123456
如果使用的是${}的话 最后的SQL为:
select* from users where username='张三'#' and password='123456'
可以看到 username=‘张三’ 之后是一个#
而在SQL中#是注释的意思,所以这条语句真正发挥作用的部分就是:select* from users where username=‘张三’ 就直接变成了一条查找张三 的语句,完全不用经过密码验证,这样就属于SQL注入!
但是如果使用的是#{},就可以避免这个问题。
因为经过sql动态解析和预编译,会把单引号转义为 ’ 那么sql最终解析为:
select* from users where username='张三\'#' and password='123456'
这样就查不出任何数据,有效阻止sql注入
性能考虑
因为预编译语句对象可以重复利用,把一个sql预编译后产生的PreparedStatement对象缓存下来,下次对于同一个sql,可以直接使用缓存的PreparedStatement对象,mybatis默认情况下,对所有的sql进行预编译,这样的话 #{}的处理方式性能会相对高些。
总结:
能使用#{}的时候尽量使用#{}!