在 Java 的 MyBatis 框架中,# 和 $ 的区别

一、基本语法

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" 导致严重后果。

相关推荐
实名上网宋凯宣7 小时前
Django
后端·python·django
利威尔xu9 小时前
Go IM 源码实战(14):本地缓存快 3000 倍,失效却难 10 倍
后端·缓存·微服务·面试·golang
逃逸线LOF10 小时前
Ribbon负载均衡原理(Irule组件、源码分析)
后端·spring cloud·ribbon
慢云智慧空间10 小时前
从设备智能到空间理解,空间智能终端解决方案商如何重新定义行业标准
后端
利威尔xu10 小时前
Go IM 源码实战(13):30 个会话只有 2 个红点,未读数错在三条路径
redis·后端·微服务·面试·golang
薛定谔的猫-菜鸟程序员11 小时前
软件架构设计师开发资料大全(从入门到精通)
后端·python·系统架构·flask·架构设计师
IT_陈寒12 小时前
SpringBoot多环境配置把我坑惨了,这个踩坑经验你最好看看
前端·人工智能·后端
码事漫谈13 小时前
DeepSeek 搞了个 "Super-agent",但不是超级智能
后端
吴凡wudianmei13 小时前
从 0 搭一个 AI Agent(四):SSE 跑通之后,三处“契约错配”与它们的修复边界
前端·后端·aigc
小兔子13 小时前
defer 的参数在写下那一刻就求值了,循环里这样写会踩坑
后端