【Java-MyBatis】MyBatis 的 $ 和 # 传参的区别?
·

MyBatis 中 $ 和 # 传参的区别详解
🌟 核心区别
想象你在餐厅点餐:
#{}像服务员帮你安全打包外卖
比如WHERE name = #{name}→ MyBatis 会预编译成WHERE name = ?,再安全填入参数值(如"张三"),防止 SQL 注入。${}像直接把食材扔进锅里
比如ORDER BY ${column}→ MyBatis 会直接拼接字符串:ORDER BY age。容易引发安全问题,但能动态处理 SQL 关键字。
📖 详细对比(附故事举例)
1. 处理方式不同
-
#{}(预编译占位符)SELECT * FROM user WHERE id = #{userId}→ 实际执行:
SELECT * FROM user WHERE id = ?
(参数userId=5会被安全填充)
✅ 场景:传递值参数(如数字、字符串)。 -
${}(字符串替换)SELECT * FROM user ORDER BY ${orderField}→ 实际执行:
SELECT * FROM user ORDER BY age
🚨 风险:若orderField="age; DROP TABLE user;",直接导致删表!
✅ 场景:动态拼接 SQL 关键字(如表名、排序字段)。
2. 安全性与性能
-
#{}更安全高效
类似「外卖打包」:- 预编译一次 SQL,多次执行只需替换参数
- 自动防止 SQL 注入(参数值会被转义)
-
${}需谨慎使用
类似「自己炒菜」:- 每次都要重新编译 SQL
- 必须手动过滤参数(如用
StringUtils.isAlphanumeric()检查字段名)
3. 实战故事:用户列表排序功能
public<User> getUsers(String sortBy) {
// 危险写法(${} 未过滤):
// String sql = "SELECT * FROM user ORDER BY ${sortBy}";
// 安全做法:
if (!Arrays.asList("name", "age", "id").contains(sortBy)) {
sortBy = "id"; // 默认排序
}
String sql = "SELECT * FROM user ORDER BY ${sortBy}";
// 更推荐用 #{} 但 MyBatis 不支持关键字占位符,所以需用 ${} + 白名单校验
}
→ 这里必须用 ${},但通过白名单过滤确保安全。
💎 总结
| 特性 | #{} | ${} |
|---|---|---|
| 原理 | 预编译占位符 (?) | 直接字符串替换 |
| 安全性 | ⭐⭐⭐⭐⭐ 防 SQL 注入 | ⭐ 需手动过滤参数 |
| 性能 | ⭐⭐⭐⭐ 可复用预编译语句 | ⭐⭐ 每次重新编译 SQL |
| 适用场景 | 传递值参数(WHERE条件值) | 动态SQL关键字(表名/排序等) |
| 类比 | 服务员打包外卖 | 自己动手炒菜 |
✨ 黄金法则:
能用#{}绝不用${}!
必须用${}时,务必通过白名单机制严格过滤输入。
更多推荐


所有评论(0)