#
默认情况下,是用#{}
会被预编译处理,会使得Mybatis创建PreparedStatement,当做占位符,参数化输入的参数,就和在sql内使用?
一样。
mybatis在处理#{}时,会将sql中的#{}替换为?
,调用PreparedStatement的set
方法来赋值。那么,一些特殊字符就会在真正执行前被转义。
比如有以下sql:
select id,name from user where id = #{id}
那么此时会将#{id}解析为参数占位符,语句预编译为
select id,name from user where id = ?
将预编译语句传给服务器后,执行时仅将具体的参数值传递给服务器执行。
也就是说,使用#{}
方法可以防止拼接方式的sql注入攻击。因为SQL注入发生在SQL编译期,但是用#{}时,sql会被预编译,此后不再变化,执行sql只是传递具体的参数值来还行。
大多数情况都是用#{},更安全和快速。
$
而${}
只会被做简单的字符串替换处理,不会做字符串转义。
比如刚才那个sql使用$,且id参数值为23:
select id,name from user where id = ${id}
则该条语句会在动态SQL解析阶段被Mybatis解析为:
select id,name from user where id = 23
也就是说直接将该变量进行了值替换,不做任何额外处理。
比如order by的时候,就必须使用${}:
ORDER BY ${columnName}
我们可以综合#{}和${}进行使用,比如有如下查找语句:
@Select("select * from user where id = #{id}")
User findById(@Param("id") long id);
@Select("select * from user where name = #{name}")
User findByName(@Param("name") String name);
@Select("select * from user where email = #{email}")
User findByEmail(@Param("email") String email);
...
很容易看出来他们的共性,所以我们可以最直接写为:
@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);
这种情况下,${column}
会被直接替换,而 #{value}
会被使用?
预处理。则在调用方法时,就十分方便了:
User userOfId1 = userMapper.findByColumn("id", 1L);
User userOfNameKid = userMapper.findByColumn("name", "kid");
User userOfEmail = userMapper.findByColumn("email", "[email protected]");
但需要注意的是,这样使用有被SQL注入攻击的风险,必须自己做权限控制或转义检验。
精讲#{}和${}的区别是什么?
Mybatis的$和#处理源码分析
mybatis-3官方文档中文版