#和$符号使用场景 注意事项

在 MyBatis 中,$# 的区别很关键,关系到 SQL 注入风险和 SQL 的动态拼接方式。其中:什么时候 $ 不能换成 #,什么时候能换?


一、先简单回顾一下 $# 的区别:

  • #安全的参数替换方式 ,使用预编译机制,会将参数作为占位符传给数据库,避免 SQL 注入。

    sql 复制代码
    SELECT * FROM user WHERE name = #{name}

    最终执行的是:

    sql 复制代码
    SELECT * FROM user WHERE name = ?

    然后由 JDBC 设置参数。

  • $直接文本替换 ,是在 SQL 语句生成阶段将参数原样拼接 进 SQL 中,容易引起 SQL 注入

    sql 复制代码
    SELECT * FROM user ORDER BY ${column}

    如果 column = "name",最终 SQL 是:

    sql 复制代码
    SELECT * FROM user ORDER BY name

二、不能直接用 # 替换 $ 的情况(即必须用 $):

这些都是必须动态拼接 SQL 结构或关键字的情况:

  1. 动态字段名或表名

    sql 复制代码
    SELECT ${column} FROM ${table}
    • 你不能写成 #{column},因为数据库不允许字段名或表名是占位符。
    • 表名/字段名不能作为参数预编译处理,只能拼接进去。
  2. 动态排序

    bash 复制代码
    ORDER BY ${sortField} ${sortOrder}
    • 如果用 #,会变成语法错误,SQL 无法执行。
  3. 动态 where 条件字段

    bash 复制代码
    WHERE ${field} = #{value}
    • 字段名不能预编译,因此必须用 ${field}

三、可以用 # 替换 $ 的情况:

  • 凡是值的部分(常规参数)都应该使用 #,而不是 $

    bash 复制代码
    WHERE id = #{id}
    AND name = #{name}
    AND created_at >= #{startTime}

只要不是拼接字段名、表名、排序等 SQL 结构,基本都可以用 #


四、小结

使用场景 推荐用法 原因说明
字段值 / 参数值 #{} 安全,防止 SQL 注入
字段名 / 表名 ${} 不能使用预编译,只能拼接
动态排序字段 / 顺序 ${} 语法需要拼接关键字
拼接完整 where 子句 ${} 需要直接拼接结构(如多个条件)

五、安全建议

如果你必须使用 ${} ,一定要白名单校验输入值,避免 SQL 注入风险。

比如:

arduino 复制代码
if (!ALLOWED_COLUMNS.contains(sortField)) {
    throw new IllegalArgumentException("Invalid column");
}
相关推荐
JAVA面经实录91710 小时前
Java企业级工程化·终极完整版背诵手册(无遗漏、全覆盖、面试+落地通用)
java·开发语言·面试
陈随易11 小时前
有生之年系列,Nodejs进程管理pm2 v7.0发布
前端·后端·程序员
许彰午12 小时前
CacheSQL(二):主从复制——OpLog 环形缓冲区与故障自动恢复
java·数据库·缓存
陈随易12 小时前
AI时代,你还在坚持手搓文章吗
前端·后端·程序员
Bat U13 小时前
JavaEE|多线程初阶(七)
java·开发语言
大鱼七成饱13 小时前
VMware NAT模式下固定内网IP(附详细图文)
后端
misL NITL14 小时前
idea、mybatis报错Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required
tomcat·intellij-idea·mybatis
IT_陈寒15 小时前
Vue的这个响应式陷阱,我debug了一整天才爬出来
前端·人工智能·后端
兔子零102416 小时前
手把手教你在 Claude Code 中接入 DeepSeek-V4
后端
掌心向暖RPA自动化16 小时前
如何获取网页某个元素在屏幕可见部分的中心坐标影刀RPA懒加载坐标定位技巧
java·javascript·自动化·rpa·影刀rpa