MyBatis 中 #{} 和 ${} 的区别详解

server/2025/3/28 16:42:29/

目录

1. #{} 和 ${} 的基本概念

1.1 #{}

1.2 ${}

2. #{} 和 ${} 的工作原理

2.1 #{} 的工作原理

2.2 ${} 的工作原理

3.共同点:动态 SQL 查询

4. 区别:处理方式和适用场景

4.1 处理方式

4.2 适用场景

(1)#{} 的适用场景

(2)${} 的适用场景

mybatis%E4%B8%AD%23%E6%98%AF%E6%80%8E%E4%B9%88%E9%98%B2%E6%AD%A2sql%E6%B3%A8%E5%85%A5%E7%9A%84-toc" name="tableOfContents" style="margin-left:40px">5.mybatis中#是怎么防止sql注入的

5.1 SQL 注入的原理

5.2 #{} 防止 SQL 注入的原理

5.3 示例对比

1 使用 #{} 的示例

正常输入

恶意输入

2 使用 ${} 的示例(对比)

正常输入

恶意输入

6.当查询数据时用动态排序时为什么用${}:


MyBatis 是一个优秀的持久层框架,它简化了数据库操作,并提供了强大的 SQL 映射功能。在 MyBatis 中,#{} 和 ${} 是两种常用的占位符,用于动态替换 SQL 语句中的参数。尽管它们看起来相似,但它们在处理方式和安全性上有显著的区别。本文将详细探讨 #{} 和 ${} 的区别,并分析它们的适用场景。


1. #{} 和 ${} 的基本概念

1.1 #{}

  • 作用:用于动态替换 SQL 语句中的参数。

  • 处理方式:MyBatis 会将 #{} 转换为 JDBC 的 PreparedStatement 参数化查询。

  • 特点

    • 使用预编译机制,防止 SQL 注入。

    • 自动对参数进行转义处理。

    • 适合传递参数值。

对于字符串会自动加 ' ',如果传入参数为简单类型,可以写 ${任意值},如果是对象,写的是${属性名}

底层解析的时候如果sql语句只包含 #{ } ,这条sql就会被解析成静态类型的语句,会把 #{ }转成 " ? ",进行数据操作时调用JDBC进行赋值

1.2 ${}

  • 作用:用于直接替换 SQL 语句中的字符串。

  • 处理方式:MyBatis 会将 ${} 直接拼接到 SQL 语句中,生成完整的 SQL 字符串。

  • 特点

    • 直接拼接字符串,存在 SQL 注入风险。

    • 不对参数进行转义处理。

    • 适合动态表名、列名等非参数值场景。

不会自动加 ' ',如果传入参数为简单类型,那么必须写 ${value},如果是对象,写的是${属性名};

底层解析的时候只要sql语句包含 ${ } ,在创建cofigration对象的时候,这条sql就会被解析成动态类型的语句,底层不会把 ${ } 转成 " ? "(不做处理),只会在使用Mapper接口代理对象进行数据操作的时候把#{ }转成 " ? ",之后再调用JDBC进行赋值,把${ }转成mapper接口的参数值,所以会存在SQL注入的问题


2. #{} 和 ${} 的工作原理

2.1 #{} 的工作原理

当 MyBatis 解析到 #{} 时,会将其转换为 JDBC 的 PreparedStatement 参数化查询。例如:

<select id="getUser" resultType="User">SELECT * FROM users WHERE username = #{username} AND password = #{password}
</select>

MyBatis 会将上述 SQL 转换为:

PreparedStatement pstmt = connection.prepareStatement("SELECT * FROM users WHERE username = ? AND password = ?"
);
pstmt.setString(1, username);
pstmt.setString(2, password);

这里的 ? 是 JDBC 的参数占位符,MyBatis 会通过 PreparedStatement 的 setStringsetInt 等方法,将参数安全地传递给数据库。

2.2 ${} 的工作原理

当 MyBatis 解析到 ${} 时,会直接将其替换为实际的参数值,并拼接到 SQL 语句中。例如:

<select id="getUser" resultType="User">SELECT * FROM users WHERE username = '${username}' AND password = '${password}'
</select>

如果 username 的值为 adminpassword 的值为 123456,MyBatis 会生成以下 SQL 语句:

SELECT * FROM users WHERE username = 'admin' AND password = '123456';

3.共同点:动态参数查询

它们的主要作用是根据传入的参数值,动态替换 SQL 语句中的部分内容,从而实现灵活的查询。

例如:

<select id="getUser" resultType="User">SELECT * FROM users WHERE username = #{username}
</select>

或者:

<select id="getUser" resultType="User">SELECT * FROM users WHERE username = '${username}'
</select>

在这两个例子中,username 是一个动态参数,MyBatis 会根据传入的参数值生成最终的 SQL 语句。


4. 区别:处理方式和适用场景

尽管 #{} 和 ${} 都用于动态查询,但它们的处理方式和适用场景有显著区别。

  • 1. #{}取值符号会自动为String类型的参数加上‘’单引号
  • 2. ${}取值符号不会自动为String加上‘’单引号

4.1 处理方式

特性#{}${}
处理方式使用 PreparedStatement 预编译直接拼接字符串
安全性防止 SQL 注入存在 SQL 注入风险
参数转义自动转义特殊字符不转义特殊字符
适用场景动态参数值动态表名、列名等非参数值场景

4.2 适用场景

(1)#{} 的适用场景

#{} 主要用于 动态参数值 的场景,例如:

  • WHERE 条件中的值:

<select id="getUser" resultType="User">SELECT * FROM users WHERE username = #{username}
</select>
  • INSERT 语句中的值:
<insert id="insertUser">INSERT INTO users (username, password) VALUES (#{username}, #{password})
</insert>
  • UPDATE 语句中的值:
<update id="updateUser">UPDATE users SET password = #{password} WHERE id = #{id}
</update>

在这些场景中,#{} 会通过 PreparedStatement 预编译机制,确保参数值的安全性,防止 SQL 注入。

(2)${} 的适用场景

${} 主要用于 动态表名、列名、排序字段等非参数值 的场景,例如:

  • 动态表名:

<select id="getData" resultType="map">SELECT * FROM ${tableName}
</select>
  • 动态列名:
<select id="getData" resultType="map">SELECT ${columnName} FROM users
</select>
  • 动态排序字段:
<select id="getUsers" resultType="User">SELECT * FROM users ORDER BY ${orderBy}
</select>
  • 运行 HTML

在这些场景中,${} 会直接替换为实际的字符串,生成最终的 SQL 语句。由于表名、列名等不是用户输入的数据,通常不会导致 SQL 注入问题。


mybatis%E4%B8%AD%23%E6%98%AF%E6%80%8E%E4%B9%88%E9%98%B2%E6%AD%A2sql%E6%B3%A8%E5%85%A5%E7%9A%84" name="5.mybatis%E4%B8%AD%23%E6%98%AF%E6%80%8E%E4%B9%88%E9%98%B2%E6%AD%A2sql%E6%B3%A8%E5%85%A5%E7%9A%84">5.mybatis中#是怎么防止sql注入的

在 MyBatis 中,#{} 能够防止 SQL 注入的核心原因是它使用了 预编译(PreparedStatement) 机制。预编译机制会将 SQL 语句和参数分开处理,确保参数值不会被解释为 SQL 代码。下面我们通过一个具体的例子来说明 #{} 是如何防止 SQL 注入的。

5.1 SQL 注入的原理

SQL 注入是一种常见的安全漏洞,攻击者通过在输入中插入恶意的 SQL 代码,篡改原始的 SQL 语句,从而执行非法的数据库操作。例如:

SELECT * FROM users WHERE username = 'admin' AND password = 'password';

如果用户输入的 password 是 ' OR '1'='1,那么最终的 SQL 语句会变成:

SELECT * FROM users WHERE username = 'admin' AND password = '' OR '1'='1';

这条语句会绕过密码验证,返回所有用户数据,因为 '1'='1' 永远为真。


5.2 #{} 防止 SQL 注入的原理

当 MyBatis 使用 #{} 时,它会将 SQL 语句和参数分开处理:

  1. SQL 语句:使用 PreparedStatement 预编译。

  2. 参数值:通过 PreparedStatement 的 setStringsetInt 等方法安全地传递给数据库。

这种机制确保了参数值不会被解释为 SQL 代码,从而防止 SQL 注入。


5.3 示例对比

1 使用 #{} 的示例

假设我们有以下 MyBatis 查询:

<select id="getUser" resultType="User">SELECT * FROM users WHERE username = #{username} AND password = #{password}
</select>
正常输入
  • 输入username = "admin"password = "123456"

  • 生成的 SQL

SELECT * FROM users WHERE username = ? AND password = ?

执行过程

  • MyBatis 会使用 PreparedStatement 预编译 SQL 语句。

  • 参数值 "admin" 和 "123456" 会通过 setString 方法安全地传递给数据库。

  • 最终执行的 SQL 语句是:

SELECT * FROM users WHERE username = 'admin' AND password = '123456';
恶意输入
  • 输入username = "admin"password = "' OR '1'='1"

  • 生成的 SQL

SELECT * FROM users WHERE username = ? AND password = ?

执行过程

  • MyBatis 仍然使用 PreparedStatement 预编译 SQL 语句。

  • 参数值 "admin" 和 "' OR '1'='1" 会通过 setString 方法安全地传递给数据库。

  • 最终执行的 SQL 语句是:

SELECT * FROM users WHERE username = 'admin' AND password = '\' OR \'1\'=\'1';

这里的 ' 被转义为 \',因此恶意代码 ' OR '1'='1 不会被解释为 SQL 语法,而是作为一个普通的字符串值处理。

2 使用 ${} 的示例(对比)

假设我们有以下 MyBatis 查询:

<select id="getUser" resultType="User">SELECT * FROM users WHERE username = '${username}' AND password = '${password}'
</select>
正常输入
  • 输入username = "admin"password = "123456"

  • 生成的 SQL

SELECT * FROM users WHERE username = 'admin' AND password = '123456';
恶意输入
  • 输入username = "admin"password = "' OR '1'='1"

  • 生成的 SQL

SELECT * FROM users WHERE username = 'admin' AND password = '' OR '1'='1';

这里的 ' OR '1'='1 被直接拼接到 SQL 语句中,导致 SQL 注入,绕过密码验证。


6.当查询数据时用动态排序时为什么用${}:

UserDao接口如下:

UserMapper.xml文件如下:

调用:

  • queryUserOrderByColumn(String column)运行结果:

  • queryUserOrderByColumn2(String column)运行结果2:

  • 看数据库中的数据,说明方法1没有成功降序排序

所以对于动态排序的sql语句,要用${},而不用#{}

对于排序正确 的代码是

如果使用#{},会自动为String类型添加单引号,变成 

所以查不到数据



http://www.ppmy.cn/server/178473.html

相关文章

【SpringBoot】MorningBox小程序的完整后端接口文档

以下是「晨光宅配」小程序的完整接口文档,涵盖了所有12个表的接口。 每个接口包括请求方法、URL、请求参数、响应格式和示例 接口文档 1. 用户模块 1.1 获取用户信息 URL: /user/{userId}方法: GET请求参数: userId (路径参数): 用户ID响应格式:{"userId": 1,&qu…

在linux服务器部署Heygem

前言&#xff1a; Heygem官方文档上提供了基于windwos系统的安装方案。在实际使用过程中个人电脑的配置可能不够。这个时候如果服务器配置够的话&#xff0c;可以尝试在服务器上装一下。但是服务器一般都是linux系统的&#xff0c;于是这篇教程就出现了… 可行性分析 通读安装…

html实现table超出宽度后滑动展示

需求:这是一个详情页面,table等标签都是在后台录入的,要求实现table表格超出屏幕宽度后,可以左右滑动展示的效果。 .knowledgeDetails table{overflow: hidden;height: auto !important;width: 100%

Python散点图(Scatter Plot):数据探索的“第一张图表”

在数据可视化领域,散点图是一种强大而灵活的工具,它能够帮助我们直观地理解和探索数据集中变量之间的关系。本文将深入探讨散点图的核心原理、应用场景以及如何使用Python进行高效绘制。 后续几篇将介绍高级技巧、复杂应用场景。 Python散点图(Scatter Plot):高阶分析、散点…

DeepSeek高校教程大合集(清华,北大,浙大,夏大,天大,湖大,天大,北师大),持续更新

大家好&#xff0c;我是吾鳴。 自从DeepSeek爆火之后&#xff0c;吾鳴就一直在收集和整理关于DeepSeek的教程报告等资料&#xff0c;也收集了有一个多月了。但是有粉丝朋友反馈说&#xff0c;有点凌乱&#xff0c;细找比较麻烦。于是乎吾鳴基于金山文档建设了一个比较简陋的资源…

HTML 列表

HTML 支持有序、无序和定义列表: HTML 列表 有序列表 第一个列表项第二个列表项第三个列表项 无序列表 列表项列表项列表项 HTML无序列表 无序列表是一个项目的列表&#xff0c;此列项目使用粗体圆点&#xff08;典型的小黑圆圈&#xff09;进行标记。 无序列表使用 <u…

信号处理等相关知识点

TDNN(时延神经网络)--CNN神经网络的基础 普通神经网络: 只包含一帧的特征向量 MFCC :用于语音特征提取的算法,提取出音色(很能区分不同人的说话声音)。 TDNN 滤波器:重要特征提取。 迁移学习 小波散射变换 (WST) 小波变换--傅里叶时间无限-》时间局域 点乘:求向…

七桥问题与一笔画问题:图论的奠基石

七桥问题与一笔画问题&#xff1a;图论的奠基石 目录 历史背景问题描述数学模型化欧拉的解决方案欧拉定理及证明一笔画问题现代应用总结 历史背景 18世纪的哥尼斯堡&#xff08;今俄罗斯加里宁格勒&#xff09;是一座被普雷格尔河分割的城市&#xff0c;河中有两个岛屿&…