一、核心差异概览
| 对比维度 | #{} (预编译占位符) | ${} (字符串拼接符) |
| 处理方式 | 参数化查询,预编译处理 | 直接字符串替换 |
| 安全性 | 天然防SQL注入 | 存在SQL注入风险 |
| 性能 | 支持预编译缓存,重复查询效率更高 | 每次生成新SQL,无法复用执行计划 |
| 数据类型 | 自动类型转换(Date/Number等) | 需要手动处理特殊类型 |
| 使用场景 | 常规参数传递(WHERE条件值) | 动态表名/列名、SQL关键字拼接等特殊场景 |
二、底层原理对比
1. #{}工作原理
原始Mapper语句
SELECT * FROM users WHERE name = #{userName}
实际执行流程
1. 解析为预编译SQL:
SELECT * FROM users WHERE name = ?
2. 通过PreparedStatement.setString(1, userName)设置参数
3. 数据库执行参数化查询
2. ${}工作原理
原始Mapper语句
SELECT * FROM ${tableName} ORDER BY ${sortField}
执行时直接拼接
SELECT * FROM user_tbl ORDER BY create_time
三、举例说明
在MyBatis中,对于sql:SELECT * FROM users WHERE name = #{userName},当userName值为"aa' or 1=1"时,MyBatis会通过预编译机制进行安全处理,具体过程如下:
1、SQL解析阶段
MyBatis会将原始SQL转换为预编译语句:
SELECT * FROM users WHERE name = ?
2、参数传递阶段
通过PreparedStatement设置参数:
pstmt.setString(1, "aa' or 1=1"); // JDBC自动处理特殊字符
3、最终执行SQL
实际发送到数据库的查询为:(单引号被转义为两个单引号,具体转义方式因数据库而异)
SELECT * FROM users WHERE name = 'aa'' and 1=1'
对比之下,若使用${}来处理时:SELECT * FROM users WHERE name = ${userName},当userName值为"'aa' OR 1=1"时,实际执行的sql为:
SELECT * FROM users WHERE name = 'aa' OR 1=1
5466

被折叠的 条评论
为什么被折叠?



