在这里插入图片描述

MyBatis 中 $# 传参的区别详解

🌟 核心区别

想象你在餐厅点餐:

  • #{} 像服务员帮你安全打包外卖
    比如 WHERE name = #{name} → MyBatis 会预编译成 WHERE name = ?,再安全填入参数值(如 "张三"),防止 SQL 注入
  • ${} 像直接把食材扔进锅里
    比如 ORDER BY ${column} → MyBatis 会直接拼接字符串:ORDER BY age容易引发安全问题,但能动态处理 SQL 关键字。

📖 详细对比(附故事举例)

1. 处理方式不同
  • #{}(预编译占位符)

    SELECT * FROM user WHERE id = #{userId}
    

    实际执行SELECT * FROM user WHERE id = ?
    (参数 userId=5 会被安全填充)
    场景:传递值参数(如数字、字符串)。

  • ${}(字符串替换)

    SELECT * FROM user ORDER BY ${orderField}
    

    实际执行SELECT * FROM user ORDER BY age
    🚨 风险:若 orderField="age; DROP TABLE user;",直接导致删表!
    场景:动态拼接 SQL 关键字(如表名、排序字段)。

2. 安全性与性能
  • #{} 更安全高效
    类似「外卖打包」:

    • 预编译一次 SQL,多次执行只需替换参数
    • 自动防止 SQL 注入(参数值会被转义)
  • ${} 需谨慎使用
    类似「自己炒菜」:

    • 每次都要重新编译 SQL
    • 必须手动过滤参数(如用 StringUtils.isAlphanumeric() 检查字段名)
3. 实战故事:用户列表排序功能
public<User> getUsers(String sortBy) {
  // 危险写法(${} 未过滤):
  // String sql = "SELECT * FROM user ORDER BY ${sortBy}";
  
  // 安全做法:
  if (!Arrays.asList("name", "age", "id").contains(sortBy)) {
    sortBy = "id"; // 默认排序
  }
  String sql = "SELECT * FROM user ORDER BY ${sortBy}";

  // 更推荐用 #{} 但 MyBatis 不支持关键字占位符,所以需用 ${} + 白名单校验
}

→ 这里必须用 ${},但通过白名单过滤确保安全。


💎 总结

特性#{}${}
原理预编译占位符 (?)直接字符串替换
安全性⭐⭐⭐⭐⭐ 防 SQL 注入⭐ 需手动过滤参数
性能⭐⭐⭐⭐ 可复用预编译语句⭐⭐ 每次重新编译 SQL
适用场景传递值参数(WHERE条件值)动态SQL关键字(表名/排序等)
类比服务员打包外卖自己动手炒菜

黄金法则
能用 #{} 绝不用 ${}
必须用 ${} 时,务必通过白名单机制严格过滤输入。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐