MyBatis中 #{} 与 ${} 的区别

MyBatis中 #{}${} 的区别

一、根本区别:预编译 vs 字符串拼接

这是两者最本质的区别,决定了它们在安全性、性能和行为上的所有不同。

特性#{}(井号占位符)${}(美元占位符)
处理机制预编译(PreparedStatement)字符串拼接/直接替换(Statement)
SQL构建方式SELECT * FROM user WHERE id = ? (先生成带?的SQL模板,再传值)SELECT * FROM user WHERE id = 1 (直接将值拼入SQL字符串)
参数处理将传入的参数安全地设置到预编译语句中,会进行类型处理(如字符串自动加引号)。将传入的参数原样替换到SQL字符串中,不做任何处理。
类比就像填空题,题目(SQL结构)固定,你只需在空白处(?)填上安全的值。就像作文题,你需要自己组织完整的语句(SQL),容易写错或引入危险内容。

二、核心差异详解

1. 安全性(最关键的区别)

  • #{}:能有效防止SQL注入。

    • 因为使用了JDBC的 PreparedStatement,参数值在预编译后传入,数据库会将其视为数据而非SQL指令的一部分。即使用户传入 1 OR 1=1,也会被当作一个完整的字符串值,而不是可执行的SQL。

    • 示例:

      -- 传入参数:id = "1 OR 1=1"
      SELECT * FROM user WHERE id = #{id}
      -- 实际执行(安全):
      SELECT * FROM user WHERE id = '1 OR 1=1'
  • ${}:无法防止SQL注入。

    • 因为它直接进行字符串替换,如果用户传入恶意参数,该参数会成为SQL语句的一部分并被数据库执行。

    • 示例:

      -- 传入参数:id = "1 OR 1=1"
      SELECT * FROM user WHERE id = ${id}
      -- 实际执行(危险!):
      SELECT * FROM user WHERE id = 1 OR 1=1
      -- 这将返回所有用户数据!

2. 性能

  • #{}:通常性能更优。

    • 预编译语句(PreparedStatement)可以被数据库缓存和重用。对于同构SQL(结构相同,参数不同),数据库服务器只需编译一次,后续执行效率高。

  • ${}:性能相对较低。

    • 每次都会生成一条全新的SQL语句,数据库需要重新解析和编译

3. 参数值的处理

  • #{}:自动处理类型和引号。

    • 如果传入的是字符串,MyBatis会自动加上单引号(');如果传入数字,则不加。你无需手动处理引号。

    <!-- 传入 name = '张三' -->
    SELECT * FROM user WHERE name = #{name}
    <!-- 转换为:SELECT * FROM user WHERE name = '张三' -->
  • ${}:原样替换,需手动处理引号。

    • 替换是“愚蠢”的字符串替换。如果值是字符串,你必须在参数值中或SQL片段中自行添加引号,否则会导致语法错误。

    <!-- 错误:如果name是字符串,替换后SQL语法错误 -->
    SELECT * FROM user WHERE name = ${name}
    <!-- 正确:需要在值中或外围加上引号 -->
    SELECT * FROM user WHERE name = '${name}'
    <!-- 或者传递的参数值本身包含引号: `"'张三'"` -->

4. 适用场景

  • #{}:适用于 WHERE 条件、SET 子句、VALUES 子句等几乎所有传递参数值的场景。应作为默认首选。

  • ${}:适用于动态传入 SQL片段**,如:

    • 表名SELECT * FROM ${tableName}

    • 列名ORDER BY ${columnName}

    • 动态排序字段ORDER BY ${orderByColumn} ${orderByDirection}

    • 数据库函数/特殊SQL关键字SELECT ${columns} FROM ...

    • 注意:使用 ${} 时,必须确保参数值绝对安全(如来自内部枚举、白名单校验),绝不能直接接收用户输入,否则风险极高。

### MyBatis 中 `#` 和 `$` 符号的区别 #### 占位符功能差异 在 MyBatis 中,`#{}` 和 `${}` 都可以作为占位符来插入参数到 SQL 语句中。然而两者的工作机制存在显著不同。 当使用 `#{}` 形式的占位符时,MyBatis 将其视为预处理语句 (PreparedStatement) 的参数绑定方式[^1]。这意味着实际的 SQL 发送到数据库之前会先由 JDBC 进行一次转义处理,从而有效防止 SQL 注入攻击的发生。 而采用 `${}` 方式,则是直接替换模板中的变量名为其对应的值字符串,并不做任何额外的安全检查或转换操作[^2]。因此,在某些特殊情况下可能会引入潜在风险。 #### 使用场景对比 由于上述特性上的差别,这两种语法适用于不同的编程环境: - 对于大多数常规查询条件构建而言,推荐优先选用 `#{}` 结构以增强应用程序的整体安全性; - 当遇到一些无法通过标准 API 实现的需求——比如表名、列名动态指定等情况时,则可能不得不借助 `${}` 完成相应逻辑编码工作;不过此时应当格外谨慎对待输入验证环节[^3]。 #### 示例代码展示 下面给出一段简单的例子说明如何分别运用这两种表达形式编写 Mapper XML 文件内的 SELECT 语句: ```xml <!-- 正确做法 --> <select id="findUserById" parameterType="int" resultType="com.example.User"> SELECT * FROM users WHERE user_id = #{userId} </select> <!-- 错误示范:容易遭受注入威胁 --> <select id="findByTableName" parameterType="string" resultType="map"> SELECT * FROM ${tableName} <!-- 不建议这样写 --> </select> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值