MyBatis中的#{}与${}的区别和应用详解
MyBatis中的#{}与${}的区别和应用详解
在使用MyBatis进行数据库操作时,经常会用到动态SQL语句。为了动态地拼接SQL,MyBatis提供了两种占位符方式:#{} 和 ${}
。这两者有着不同的用法和特性,在实际开发中需要根据具体的场景选择使用哪一种。在这篇博客中,我们将详细解析#{}和${}
的区别和应用,并结合实际例子加深理解。
一、#{}与${}的基础介绍
1.1 #{}的使用
#{}
是MyBatis中的占位符语法,表示参数值会被替换成SQL语句中的参数,同时它会将传入的参数进行预编译。MyBatis会自动为参数值做类型转换,并且防止SQL注入。具体来说,`#{}会通过PreparedStatement绑定参数,在SQL执行前对参数进行处理和转义,从而避免了SQL注入的问题。
示例:使用#{}进行参数传递
<select id="getUserByName" parameterType="String" resultType="User">
SELECT * FROM users WHERE username = #{username}
</select>
假设传入的username
参数为zhangxiangwei
,最终执行的SQL为:
SELECT * FROM users WHERE username = 'zhangxiangwei'
注意,`#{}会自动为字符串参数加上单引号,从而防止SQL注入。
1.2 ${}的使用
${}
是MyBatis中的另一种占位符语法,它不会对参数进行预编译,而是直接将参数值拼接到SQL语句中。由于没有预编译,${}
存在SQL注入的风险,因此使用时必须谨慎。
示例:使用${}进行参数传递
<select id="getUserByField" parameterType="String" resultType="User">
SELECT ${fieldName} FROM users WHERE user_age = 22
</select>
如果传入的fieldName
参数为user_name
,那么最终执行的SQL为:
SELECT user_name FROM users WHERE user_age = 22
这里,user_name
直接拼接到SQL语句中,而没有加上单引号,且没有进行任何类型转换。
二、#{}与${}的区别
2.1 安全性
- #{}:使用
#{}
时,MyBatis会对传入的参数进行预编译,并自动转义参数内容,这样可以有效防止SQL注入攻击。 - ${}:使用
${}
时,传入的参数会直接拼接到SQL中,无法进行参数值的转义或预编译,这就导致了SQL注入的风险。必须特别注意,如果使用${}
,你需要确保传入的参数是可信的。
2.2 适用场景
- #{}:适用于传入具体的字段值、常规的查询条件等。大多数情况,尤其是涉及用户输入时,推荐使用
#{}
- ${}:通常用于动态拼接字段名、表名等不可预见的内容。当字段或表名需要动态变化时,使用
${}
可以实现动态拼接。
2.3 性能
- #{}:使用`#{}时,MyBatis会生成预编译的SQL语句,能够提高性能,因为数据库可以缓存预编译语句并避免每次都解析SQL。
- ${}:每次使用
${}
时,MyBatis都会生成新的SQL语句并直接发送给数据库执行,这可能会影响性能,尤其是在高并发环境下。
2.4 SQL注入风险
- #{}:由于采用了预编译,
#{}
能够有效防止SQL注入。 - ${}:由于直接拼接参数值,
${}
极易引发SQL注入攻击。如果不加防范措施,恶意用户可以通过构造特殊输入来修改SQL语句,进行非法操作。
三、实际应用中的选择
3.1 使用#{}防止SQL注入
场景:当需要传递查询条件值、ID、字符串等数据时,应优先使用`#{},如查询用户时传入用户名。
<select id="getUserByName" parameterType="String" resultType="User">
SELECT * FROM users WHERE username = #{username}
</select>
优点:
- 自动防止SQL注入
- 支持类型转换和参数转义
3.2 使用${}进行动态SQL拼接
场景:当需要动态指定表名或字段名时,可以使用${}
。例如,生成动态排序SQL时。
<select id="getUsersWithDynamicSorting" parameterType="map" resultType="User">
SELECT * FROM users
WHERE 1=1 <!-- 为了方便拼接,可以始终使用一个始终成立的条件 -->
<if test="sortField != null and sortOrder != null">
ORDER BY ${sortField} ${sortOrder}
</if>
</select>
这里,sortField
和sortOrder
是动态传入的值。使用${}
来直接拼接排序字段和排序顺序,避免了#{}
无法用于列名和排序字段的局限。
注意:在使用${}
时,务必确保传入的字段或表名是可信的。如果sortField
来自用户输入,可能会引发SQL注入风险。
3.3 使用#{}实现模糊查询
当使用#{}
进行模糊查询时,可以结合LIKE
语句来实现,避免SQL注入。
<select id="searchUser" parameterType="String" resultType="User">
SELECT * FROM users WHERE username LIKE CONCAT('%', #{username}, '%')
</select>
在这个例子中,#{}
会安全地传递username
参数,并将其包含在LIKE
查询条件中。
四、总结
- #{} 是MyBatis中最常用的占位符,适用于大多数情况,能够防止SQL注入并提高SQL性能。
- ${} 适用于动态拼接表名、字段名等结构性内容,但要小心SQL注入风险。
- 在实际开发中,尽量使用
#{}
,只在无法使用#{}
的情况下(如动态列名、表名等)使用${}
。 - 动态SQL时,尤其是涉及排序等操作时,使用
${}
来拼接字段名和排序方式。
通过掌握#{}和${}
的使用方式,可以在保证代码安全性的同时,灵活地进行动态SQL的拼接和查询。