Mybatis中# 和 $的区别

在 MyBatis 中,#$ 都是用于在 SQL 语句中引用参数的符号,但它们的参数处理方式、安全性和适用场景有本质区别,核心差异在于是否使用预编译。

1. 处理方式不同

  • #{}:预编译处理(推荐) MyBatis 会将 #{参数名} 替换为 SQL 中的 ?(占位符),然后通过 PreparedStatement 对 SQL 进行预编译,再将参数值设置到占位符中。例如:

    XML 复制代码
    <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 语句中,不经过预编译。例如:

    XML 复制代码
    <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},但需注意参数校验)。

示例对比

假设需要查询 "年龄大于指定值" 的用户:

#{} (安全)
XML 复制代码
<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'(条件无效,查询结果正确)。
${} (不安全)
XML 复制代码
<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 结构时使用 ${} ,且务必对 ${} 的参数进行严格校验(如白名单限制)。

相关推荐
爬山算法几秒前
Netty(19)Netty的性能优化手段有哪些?
java·后端
love is sour13 分钟前
深入浅出 jmap:Java 内存分析的“显微镜“
java·开发语言·测试工具·性能优化
想用offer打牌15 分钟前
虚拟内存与寻址方式解析(面试版)
java·后端·面试·系统架构
代码or搬砖17 分钟前
SQL核心语法总结:从基础操作到高级窗口函数
java·数据库·sql
月明长歌23 分钟前
【码道初阶】【Leetcode94&144&145】二叉树的前中后序遍历(非递归版):显式调用栈的优雅实现
java·数据结构·windows·算法·leetcode·二叉树
杰克尼43 分钟前
蓝桥云课-5. 花灯调整【算法赛】
java·开发语言·算法
wanghowie1 小时前
01.02 Java基础篇|核心数据结构速查
java·开发语言·数据结构
乂爻yiyao1 小时前
java并发演进图
java
java1234_小锋1 小时前
Redis6为什么引入了多线程?
java·redis
9号达人1 小时前
支付成功订单却没了?MyBatis连接池的坑我踩了
java·后端·面试