#{}和${}是Mybatis中进行SQL语句拼接的符合。但是两者又有区别,使用${}有SQL注入的风险,使用#{}可以有效的防止SQL注入,并且是以预编译的方式传入,多次重复使用的SQL效率更高。
${}
使用示例:
<mapper namespace="cn.xh.dao">
<select id="getCount" parameterType="String" resultType="int">
select count(*) from ${value}
</select>
</mapper>
${value}会被传入的内容替换,注意此处是直接替换(也就是字符串拼接),不会加上引号。例如传入的内容为 user 则sql语句变为:select count(*) from user
运行日志如下:
SQL注入问题:
现在我们把传入的内容改一下,改成user; DROP TABLE users;
sql语句变为select count(*) from user;DROP TABLE users;这两个sql都能执行成功。这是非常危险的,原本我们只是想查询一下user表的记录数,结果被SQL注入,连整个user表都被删除了。
那么造成SQL注入的本质原因是什么呢?
答案是:代码和数据被混淆了。所以需要有一个专门的占位符来代表用户传入的数据。
#{}
#{}占位符就是专门用来作为用户数据占位符。他对用户传入的数据做了引号处理,就算用户进行SQL注入攻击,用户输入的SQL也不会生效。
<mapper namespace="cn.xh.dao">
<select id="getCount" parameterType="String" resultType="int">
select count(*) from user where age = #{value}
</select>
</mapper>
#{value}会被传入的内容替换,替换的时候将传入的内容当成字符串,加上引号:例如传入的内容为23,sql语句会变为select count(*) from user where age= ‘23’
看一下,运行日志
最关键的是加上引号,这样就能防止SQL注入的问题。
现在来讲讲#{}和${},#{}进行预编译处理使用到了PreparedStatement,${}则是直接拼接SQL语句使用了Statement,下面讲讲两者存在哪些区别。
PreparedStatement和Statement的区别
Statement会调用它的executeQuery方法,将sql语句以字符串的形式传入这个方法中,这个方法会返回数据库的结果集。这种方式调用一次方法只能传入一段确定的sql语句,因为这段sql语句是确定的,所以它只是满足了该次调用的需求,无法进行复用,如果需要写入其他的sql语句就要重新键入新的sql语句。
PrepareStament的方式呢,它在创建对象时就要将所用到的sql语句作为参数传入其中,被传入的sql语句并不是准确的sql语句,它所传入的sql语句可以说是个sql填空题,这里会用到“?”这个占位符。
PreareStatement prearestatement = Connection.preareStatement(“select ? from table;”);
请注意,“select ? from table;” 发送到服务器,其中没有任何数据!数据库会对传入的sql语句进行预编译。然后,我们通过第二个请求发送数据,该请求实际上与查询本身是分开的。
$db->execute($data);
所以这解决了,SQL注入的安全问题,同时如果多次使用到了相同预编译的语句,还能提高效率。