MyBatis 中 #{} 和 ${} 的区别详解
目录
1. #{} 和 ${} 的基本概念
1.1 #{}
1.2 ${}
2. #{} 和 ${} 的工作原理
2.1 #{} 的工作原理
2.2 ${} 的工作原理
3.共同点:动态 SQL 查询
4. 区别:处理方式和适用场景
4.1 处理方式
4.2 适用场景
(1)#{} 的适用场景
(2)${} 的适用场景
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
的 setString
、setInt
等方法,将参数安全地传递给数据库。
2.2 ${}
的工作原理
当 MyBatis 解析到 ${}
时,会直接将其替换为实际的参数值,并拼接到 SQL 语句中。例如:
<select id="getUser" resultType="User">
SELECT * FROM users WHERE username = '${username}' AND password = '${password}'
</select>
如果 username
的值为 admin
,password
的值为 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 注入问题。
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 语句和参数分开处理:
-
SQL 语句:使用
PreparedStatement
预编译。 -
参数值:通过
PreparedStatement
的setString
、setInt
等方法安全地传递给数据库。
这种机制确保了参数值不会被解释为 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.kler.cn/a/596469.html 如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!