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

207 阅读1分钟

一、基本语法

<select id="getUserByName" resultType="User">
    SELECT * FROM user WHERE username = #{name}
</select>
<select id="getUserByTable" resultType="User">
    SELECT * FROM ${table} WHERE id = #{id}
</select>

二、区别详解

特性# 占位符$ 占位符
替换方式预编译,使用 PreparedStatement 参数直接字符串替换
SQL 安全性安全(防止 SQL 注入)不安全(容易被 SQL 注入)
使用场景绝大多数参数替换,如 WHERE 条件中的值表名、列名、排序字段等结构性元素
示例WHERE name = #{name}WHERE name = ?,再将参数安全绑定为 adminORDER BY ${column}ORDER BY username

三、举个例子理解差异

1. 使用 #

<select id="findUser" resultType="User">
    SELECT * FROM user WHERE username = #{username}
</select>

传入 username = "admin"
生成的 SQL 为:

SELECT * FROM user WHERE username = ?

然后通过 JDBC 的 PreparedStatementadmin 安全地绑定进去。
可以防止 SQL 注入,因为参数不会直接拼接进 SQL。


2. 使用 $

<select id="findUser" resultType="User">
    SELECT * FROM user WHERE username = '${username}'
</select>

传入 username = "admin' OR '1'='1"
生成的 SQL 为:

SELECT * FROM user WHERE username = 'admin' OR '1'='1'

❌ 这样就变成了 SQL 注入,永远返回真,导致严重安全漏洞。


四、典型使用场景

场景使用方式原因
传递查询条件、值#{}安全,防注入
动态表名、字段名${}结构性语法,不能预编译,只能拼接
排序字段${}例如 ORDER BY ${sortColumn}
SQL 片段(比如动态列名)${}用于构建 SELECT 字段列表等

五、总结口诀

  • 凡是值,尽量用 #{}
  • 凡是结构(表名、列名、排序字段),才考虑 ${},但一定要控制输入!

六、一个防坑建议

如果确实要使用 ${}(比如动态列名),一定要做白名单过滤

// 假设只允许排序字段为 id 或 name
if (!"id".equals(sortColumn) && !"name".equals(sortColumn)) {
    throw new IllegalArgumentException("非法字段");
}

否则就可能被用户输入类似 "id; DROP TABLE user" 导致严重后果。