在MyBatis中$和#有什么区别

在 MyBatis 中,${}#{} 是两种处理 SQL 参数的占位符,它们在实现机制、安全性、使用场景上存在显著差异。以下是详细对比:

核心区别对比

特性 #{} ${}
底层机制 预编译占位符(PreparedStatement 字符串直接替换
安全性 ✅ 防止 SQL 注入 ❌ 存在 SQL 注入风险
参数处理 自动添加引号(字符串/日期类型) 需手动添加引号(否则语法错误)
性能 较低(预编译开销) 较高(直接拼接 SQL)
适用场景 动态参数值(如 WHERE id = ? 动态 SQL 片段(如表名、排序字段)

机制详解

  1. #{}(预编译占位符)

    • MyBatis 会将其解析为 JDBC 的?占位符,通过PreparedStatement预编译 SQL。

    • 参数值会被安全转义,例如:

      sql 复制代码
      SELECT * FROM users WHERE name = #{name}  -- 转换为:SELECT * FROM users WHERE name = ?

      若name = "John",实际执行时参数值会被安全绑定为'John'。

  2. ${}(字符串替换)

    • 直接替换为参数值的字面量,无预编译或转义。

    • 例如:

      sql 复制代码
      SELECT * FROM users WHERE id = ${id}  -- 若 id=1,替换为:SELECT * FROM users WHERE id = 1

      若id = "1 OR 1=1",则 SQL 会变为:

      java 复制代码
      SELECT * FROM users WHERE id = 1 OR 1=1  -- 查询所有数据,存在 SQL 注入风险[1,5](@ref)

安全性问题

  • #{}:天然防 SQL 注入,适用于用户输入或外部参数。

  • ${}:高风险!仅适用于完全可控的静态值(如内部生成的表名、列名)。

    错误示例(模糊查询):

    复制代码
    SELECT * FROM products WHERE name LIKE '%${keyword}%' -- 若 keyword="' OR 1=1 --",导致数据泄露!

使用场景对比

#{} 的适用场景
  • 普通条件查询(值动态传递):

    sql 复制代码
    SELECT * FROM orders WHERE user_id = #{userId} [1,4](@ref)
  • 日期/字符串参数(自动添加引号):

    sql 复制代码
    INSERT INTO logs (content) VALUES (#{logContent})  -- 自动转为 'xxx' [7,8](@ref)
  • 模糊查询(安全写法):

    sql 复制代码
    SELECT * FROM products WHERE name LIKE CONCAT('%', #{keyword}, '%') [2,3](@ref)
⚠️ ${} 的适用场景
  • 动态表名/列名(SQL 片段不可预编译):

    sql 复制代码
    SELECT * FROM ${tableName} WHERE ${column} = 1 [3,5,7](@ref)
  • 排序字段(如ORDER BY ${sortField}):

    sql 复制代码
    SELECT * FROM users ORDER BY ${orderBy} DESC [3,7](@ref)
  • 批量操作(如IN子句):

    sql 复制代码
    DELETE FROM cart WHERE id IN (${ids})  -- ids="1,2,3" [7](@ref)

最佳实践与避坑指南

  1. 默认使用 #{}:除非必须动态拼接 SQL 片段,否则一律用#{}确保安全。

  2. ${} 的防御措施:

    • 仅允许传入白名单值(如预定义表名列表)。

    • 手动过滤危险字符(如空格、分号)。

  3. 模糊查询的替代方案:

    • 用CONCAT函数(推荐):

      复制代码
      SELECT * FROM table WHERE name LIKE CONCAT('%', #{text}, '%')
    • 程序层拼接(Java 中生成"%text%"再传入)。


💎 总结

  • #{} = 安全优先 :处理动态值(用户输入、条件参数),预编译防注入

  • ${} = 谨慎使用 :处理动态 SQL 片段(表名、排序),需严格校验输入

关键口诀:**"值用井号(#),结构用刀($)"------值动态用 #{},SQL 结构动态用 ${}。实际开发中,95% 的场景应使用#{},仅在必要时(如分表)谨慎使用${}。

相关推荐
飞翔的佩奇2 小时前
基于SpringBoot+MyBatis+MySQL+VUE实现的房屋交易平台管理系统(附源码+数据库+毕业论文+部署教程+配套软件)
数据库·spring boot·mysql·vue·毕业设计·mybatis·房屋交易平台
wb1898 小时前
服务器的Mysql 集群技术
linux·运维·服务器·数据库·笔记·mysql·云计算
zl0_00_08 小时前
web刷题2
数据库
SelectDB10 小时前
森马服饰从 Elasticsearch 到阿里云 SelectDB 的架构演进之路
大数据·数据库·数据分析
寒士obj10 小时前
MySQL偏门但基础的面试题集锦
数据库·mysql
唐叔在学习10 小时前
9类主流数据库 - 帮你更好地进行数据库选型!
数据库·redis·mysql·mongodb·nosql·大数据存储
失因11 小时前
Linux 权限管理与 ACL 访问控制
linux·运维·服务器·数据库·centos
CodeUp.11 小时前
基于SpringBoot的OA办公系统的设计与实现
spring boot·后端·mybatis
cookqq12 小时前
mongodb源代码分析创建db流程分析
数据库·sql·mongodb·nosql
yh云想12 小时前
存储函数与触发器:数据库自动化与业务逻辑封装的核心技术
数据库·sql