Mybatis中#{}和${}的区别

264 阅读1分钟

#{}和${}是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

运行日志如下:

img

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’

看一下,运行日志

img

最关键的是加上引号,这样就能防止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注入的安全问题,同时如果多次使用到了相同预编译的语句,还能提高效率。