MyBatis动态sql之${}和#{}区别
动态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进行预编译,这样的话 #{}的处理方式性能会相对高些。 总结:
能使用#{}的时候尽量使用#{}!