当前位置: 首页 > news >正文

MyBatis 核心精讲:#{} 和 ${} 的区别、使用场景及原理

前言

在 MyBatis 开发中,#{} 和 ${} 是参数绑定的两种核心语法,看似都能传递参数,底层原理、安全性、使用场景天差地别。

新手最容易混淆两者,导致 SQL 注入、参数报错、SQL 语法异常等问题。本文从底层原理、语法区别、安全性、使用场景、实战示例全方位讲解,帮你彻底吃透。

一、核心结论(先记牢)

对比项 #{} ${}
底层实现 预编译 SQL,使用 ? 占位符 直接字符串拼接,不做预处理
SQL 注入 安全,可防注入 不安全,无法防注入
参数类型 自动识别类型(字符串自动加引号) 纯文本拼接,不处理类型
使用场景 90% 场景:查询条件、插入值、更新值 表名 / 列名动态传入、排序字段、SQL 片段
性能 高,支持 SQL 缓存 低,每次都编译新 SQL

一句话总结:

能用 #{} 绝对不用 ${};只有动态传入表名 / 列名 / 排序关键字时,才被迫用 ${}。

二、底层原理解析

3.1 #{ }:预编译模式(PreparedStatement)

MyBatis 会将 #{} 替换为 JDBC 预编译的 ? 占位符。

示例:

<select id="selectById" resultType="User">select * from user where id = #{id}
</select>

最终执行的 SQL:

sql
select * from user where id = ?
  • 参数会安全赋值,自动处理数据类型

  • 字符串会自动加单引号 ' '

  • 数据库只编译一次 SQL,多次复用

3.2 ${ }:字符串直接拼接

MyBatis 会把 ${} 里的参数直接拼接到 SQL 中,不做任何处理。

示例:

<select id="selectById" resultType="User">select * from user where id = ${id}
</select>

传入 id=1,最终 SQL:

select * from user where id = 1

三、最关键区别:安全性(SQL 注入)

3.1 #{ } 防 SQL 注入

假设查询语句:


select * from user where username = #{username}

恶意传入:' or '1'='1

#{} 处理后:
select * from user where username = ?

参数被当作普通字符串,无法破坏 SQL 结构 ✅

3.2 ${ } 存在严重注入风险

同样语句:
select * from user where username = ${username}

恶意传入:' or '1'='1

拼接后 SQL:

select * from user where username = '' or '1'='1'

直接查询全表数据,数据泄露! ❌

四、字符串参数的巨大区别

4.1 使用 #{}(正确)

select * from user where username = #{username}

传入:张三

执行 SQL:

select * from user where username = '张三'

✅ 自动加单引号,不会报错

3.2 使用 ${}(必须手动加引号)

select * from user where username = '${username}'

否则会报SQL语法错误

五、必须使用 ${ } 的场景(重点)

#{} 无法替换 表名、列名、排序关键字、SQL 关键字,这些场景必须用 ${}。

场景 1:动态表名

调用:tableName = "user"执行:select * from user where id=?

场景 2:动态排序字段 / 排序方式

<select id="selectList" resultType="User">select * from userorder by ${orderBy} ${sortType}
</select>
传入:orderBy="create_time", sortType="desc"执行:order by create_time desc

场景 3:动态列名查询

select ${columnName} from user

六、实战开发规范(必看)

  1. 查询条件、插入值、更新字段值 → 一律用 #{}

  2. 动态表名、动态列名、动态排序 → 必须用 ${}

  3. 使用 ${} 必须做参数校验,防止 SQL 注入

  4. 禁止用户可控的参数直接使用 ${}(如前端传入的表名字段)

七、总结

  1. #{} 是预编译,安全,自动处理类型和引号,适合 90% 场景

  2. ${} 是字符串拼接,不安全,不处理引号,只适合动态表名 / 列名

  3. 优先使用 #{},不得已才用 ${}

  4. 使用 ${} 必须做好安全校验

http://www.jsqmd.com/news/666832/

相关文章:

  • 3个核心突破:GEMMA如何重新定义基因组关联分析的工作流
  • 视频转PPT终极指南:5分钟智能提取,告别手动截图的烦恼
  • 汇川HMI: 使用符号IO域实现画面切换
  • 如何快速掌握OpenSPG知识图谱引擎:从入门到实战的完整指南
  • 高效数据迁移:艾尔登法环存档管理工具的技术实现与最佳实践
  • 别再死记硬背MOSFET工作区了!用CMOS射频开关的视角,重新理解线性区与饱和区
  • YOLO11和dlib实战:如何用Python在10分钟内搞定一个简易疲劳检测脚本?
  • AI Agent时代的职场生存:为什么你的同事被裁了,而你还在?
  • 给SoC新手的AHB总线选型指南:AMBA2 AHB2和AMBA3 AHB-Lite到底怎么选?
  • 科研人效率工具:用Zotero Scholar Citations插件一键追踪文献影响力
  • JAVA低空经济无人机飞手接单小程序源码uniapp开源代码
  • 融合物理与神经网络电池健康管理
  • Node-RED Modbus实战:从零构建工业数据采集与控制系统
  • 别再为认证头疼了!用Slack零成本搞定Outline知识库的第三方登录(Docker部署避坑实录)
  • STM32/51单片机通用:TM1638数码管按键驱动代码详解(附16键组合键处理)
  • 5大智能功能重塑你的英雄联盟游戏体验:League Akari深度解析
  • 我是如何用9款AI工具,一键改重降重,轻松搞定毕业论文? - 麟书学长
  • AGI内容合规红线首次量化:2026奇点大会发布的《生成式运营安全阈值白皮书》(含6大监管场景实时预警逻辑)
  • 别再只抄电路图了!手把手教你为FOC电机驱动选对IR2106的自举电容和二极管
  • 如何在Mac上免费解锁百度网盘SVIP下载速度:完整指南
  • 如何免费解密中兴光猫配置文件:3步掌握家庭网络管理权
  • 碧蓝航线自动化脚本:3步快速实现智能挂机,轻松解放双手 [特殊字符]
  • 2026奇点大会未公开议程泄露:AGI产品设计的4个临界点、2个红区警告与1套压力测试工具包
  • 2026 年 4 月 GEO 优化公司评测:七家口碑服务推荐评价排名领先营销预算浪费痛点注意事项 - 速递信息
  • 从香港天文台数据到C程序:我是如何手动校对2033年农历的(避坑指南)
  • Claude Code如何通过生态项目提升效率?4个超强项目完整解析与使用指南
  • 别再死记硬背了!用RabbitMQ Web管理界面,5分钟搞懂Topic通配符的匹配规则
  • 如何高效构建大规模3D数据集:Objaverse-XL完整使用指南
  • NCBI基因组数据批量下载架构深度解析:高效获取生物信息学数据的5大核心策略
  • 2026滕州口腔那么多该选择哪家,于长立口腔实测 - 速递信息