一、基本语法
xml
<select id="getUserByName" resultType="User">
SELECT * FROM user WHERE username = #{name}
</select>
xml
<select id="getUserByTable" resultType="User">
SELECT * FROM ${table} WHERE id = #{id}
</select>
二、区别详解
特性 | # 占位符 |
$ 占位符 |
---|---|---|
替换方式 | 预编译 ,使用 PreparedStatement 参数 |
直接字符串替换 |
SQL 安全性 | ✅ 安全(防止 SQL 注入) | ❌ 不安全(容易被 SQL 注入) |
使用场景 | 绝大多数参数替换,如 WHERE 条件中的值 |
表名、列名、排序字段等结构性元素 |
示例 | WHERE name = #{name} → WHERE name = ? ,再将参数安全绑定为 admin |
ORDER BY ${column} → ORDER BY username |
三、举个例子理解差异
1. 使用 #
:
xml
<select id="findUser" resultType="User">
SELECT * FROM user WHERE username = #{username}
</select>
传入 username = "admin"
生成的 SQL 为:
sql
SELECT * FROM user WHERE username = ?
然后通过 JDBC 的 PreparedStatement
将 admin
安全地绑定进去。
✅ 可以防止 SQL 注入,因为参数不会直接拼接进 SQL。
2. 使用 $
:
xml
<select id="findUser" resultType="User">
SELECT * FROM user WHERE username = '${username}'
</select>
传入 username = "admin' OR '1'='1"
生成的 SQL 为:
sql
SELECT * FROM user WHERE username = 'admin' OR '1'='1'
❌ 这样就变成了 SQL 注入,永远返回真,导致严重安全漏洞。
四、典型使用场景
场景 | 使用方式 | 原因 |
---|---|---|
传递查询条件、值 | #{} |
安全,防注入 |
动态表名、字段名 | ${} |
结构性语法,不能预编译,只能拼接 |
排序字段 | ${} |
例如 ORDER BY ${sortColumn} |
SQL 片段(比如动态列名) | ${} |
用于构建 SELECT 字段列表等 |
五、总结口诀
- 凡是值,尽量用
#{}
! - 凡是结构(表名、列名、排序字段),才考虑
${}
,但一定要控制输入!
六、一个防坑建议
如果确实要使用 ${}
(比如动态列名),一定要做白名单过滤:
java
// 假设只允许排序字段为 id 或 name
if (!"id".equals(sortColumn) && !"name".equals(sortColumn)) {
throw new IllegalArgumentException("非法字段");
}
否则就可能被用户输入类似 "id; DROP TABLE user"
导致严重后果。