Mybatis中# 和 $的区别
在 MyBatis 中,# 和 $ 都是用于在 SQL 语句中引用参数的符号,但它们的参数处理方式、安全性和适用场景有本质区别,核心差异在于是否使用预编译。
1. 处理方式不同
- #{}:预编译处理(推荐)MyBatis 会将- #{参数名}替换为 SQL 中的- ?(占位符),然后通过 PreparedStatement 对 SQL 进行预编译,再将参数值设置到占位符中。例如:- <select id="getUserById" resultType="User">SELECT * FROM user WHERE id = #{userId} </select>- 实际执行时,SQL 会先被预编译为: - SELECT * FROM user WHERE id = ?然后将- userId的值(如 100)通过- PreparedStatement.setInt(1, 100)方式设置到占位符,最终执行的 SQL 是:- SELECT * FROM user WHERE id = 100
- ${}:字符串直接替换(不推荐)MyBatis 会直接将- ${参数名}替换为参数的原始值,拼接到 SQL 语句中,不经过预编译。例如:- <select id="getUserById" resultType="User">SELECT * FROM user WHERE id = ${userId} </select>- 若 - userId的值为 100,SQL 会直接拼接为:- SELECT * FROM user WHERE id = 100(若参数是字符串,需要手动加引号,如- ${username}需写成- '${username}')
2. 安全性不同
- #{}:防SQL注入(安全)由于使用预编译,参数值会被当作 “数据” 处理,而非 SQL 语句的一部分。即使参数包含恶意 SQL 片段,也不会被执行。例如:若- userId传入恶意值- 100 OR 1=1,- #{}会将其作为整体参数,最终 SQL 为:- SELECT * FROM user WHERE id = '100 OR 1=1'(查询条件无效,不会返回所有数据)。
- ${}:存在SQL注入风险(不安全)由于直接拼接字符串,若参数包含恶意 SQL 片段,会被解析为 SQL 的一部分执行。例如:若- userId传入- 100 OR 1=1,- ${}会拼接为:- SELECT * FROM user WHERE id = 100 OR 1=1(条件恒成立,返回所有用户数据,造成信息泄露)。
3. 适用场景不同
- #{}:适用于大多数参数传递场景只要是用户输入的参数(如查询条件、新增 / 修改的字段值等),都应该用- #{},优先保证安全性。例如:查询、新增、修改、删除操作中的参数(- WHERE条件、- INSERT的值、- UPDATE的字段等)。
- ${}:仅用于动态拼接SQL片段(非用户输入)当需要动态生成 SQL 的 “结构”(而非数据)时使用,且参数必须是可信的(非用户输入,或已严格校验)。常见场景:- 动态表名(如分表场景:SELECT * FROM user_${tableSuffix});
- 动态排序字段(如 ORDER BY ${sortColumn},需确保sortColumn只能是指定列名,避免注入);
- 动态 SQL 关键字(如 LIMIT ${pageSize},但需注意参数校验)。
 
- 动态表名(如分表场景:
示例对比
假设需要查询 “年龄大于指定值” 的用户:
用 #{} (安全)
<select id="getUserByAge" resultType="User">SELECT * FROM user WHERE age > #{minAge}
</select>
- 若 minAge传入18 OR 1=1,实际执行的 SQL 是:SELECT * FROM user WHERE age > '18 OR 1=1'(条件无效,查询结果正确)。
用 ${} (不安全)
<select id="getUserByAge" resultType="User">SELECT * FROM user WHERE age > ${minAge}
</select>
- 若 minAge传入18 OR 1=1,实际执行的 SQL 是:SELECT * FROM user WHERE age > 18 OR 1=1(条件恒成立,返回所有用户,造成注入)。
总结
| 特性 | #{} | ${} | 
|---|---|---|
| 处理方式 | 预编译,替换为 ?占位符 | 直接字符串替换,拼接 SQL | 
| 安全性 | 防 SQL 注入(安全) | 存在 SQL 注入风险(不安全) | 
| 参数类型 | 自动加引号(字符串类型) | 需手动加引号(字符串类型) | 
| 适用场景 | 大多数参数传递(用户输入) | 动态 SQL 结构(表名、排序字段等) | 
最佳实践:优先使用 #{} ,仅在必须动态拼接 SQL 结构时使用 ${} ,且务必对 ${} 的参数进行严格校验(如白名单限制)。
