引言:在使用 MyBatis 进行数据库操作时,#{}
和 ${}
的区别是面试中常见的问题,对理解如何在 MyBatis 中安全有效地处理 SQL 语句至关重要。正确使用这两种占位符不仅影响应用的安全性,还涉及到性能优化。
题目
被拷打已老实!面试官问我 #{} 和 ${} 的区别是什么?
推荐解析
预编译与直接替换
面试官问了我这个问题,直接把我问老实了,求放过,赶忙来写下以下思考:
1)#{}
:使用 #{}
时,MyBatis 为 SQL 语句的参数生成一个预编译语句,并通过参数占位符(?
)来代替实际的参数值。这种方式是安全的,因为它防止了 SQL 注入攻击,参数值在发送到数据库之前不会被解析为 SQL 的一部分。
如果你有一个查询用户的操作,你可以这样写:
SELECT * FROM users WHERE id = #{userId}
2)${}
:相反,使用 ${}
时,参数值被直接替换到 SQL 语句中。这种方式虽然灵活,可以动态替换表名、列名等 SQL 语句的组成部分,但却可能导致 SQL 注入问题,因为参数值如果包含恶意的 SQL 代码也会被执行。
如果你需要动态地选择表名或列名,可以这样使用:
SELECT * FROM ${tableName}
性能考虑
对于性能我得出以下思考:
1)#{}
性能优势:由于使用预编译的 SQL 语句,数据库可以重用相同结构的 SQL 语句,减少编译次数,提高执行效率。
2)${}
性能问题:可能导致每次都要编译新的 SQL 语句,因为 SQL 结构可能因为参数的不同而改变。
类型处理
琢磨琢磨我又发现了新大陆!类型处理~
1)#{}
类型处理:MyBatis 可以根据参数的 Java 类型自动推断数据库类型,处理日期、枚举等复杂类型的转换。
2)${}
类型处理:不进行类型转换,将参数视为纯文本,直接嵌入到 SQL 语句中。
鱼聪明 AI 的回答:
鱼聪明 AI 地址:https://www.yucongming.com/
#{}(参数占位符)
- 预编译:
#{}
表达式使得参数在 SQL 语句中以占位符?
的形式出现。这意味着 SQL 语句会被预编译并多次执行,参数值在运行时绑定。这种方式对于数据库性能优化至关重要,因为预编译可以减少数据库编译SQL的次数,提高执行效率。 - 防止 SQL 注入:由于参数不是直接拼接到 SQL 语句中,而是作为一个绑定的值传递给预编译的 SQL 语句,因此
#{}
防止了 SQL 注入的风险。用户输入被当作字符串处理,不会被执行为 SQL 代码的一部分。 - 类型安全:MyBatis 能够根据 Java 类型自动推断出相应的 SQL 类型。例如,如果你传递一个 Java
Date
对象,MyBatis 会适当地处理它与 SQL 日期类型之间的转换。
${}
(SQL 字符串拼接)
- 直接替换:使用
${}
时,参数值会在解析 SQL 语句之前直接替换到 SQL 字符串中。这意味着你可以通过${}
插入动态表名、列名或者其他 SQL 片段,如动态排序条件。 - 高风险:
${}
由于是直接将参数内容拼接到 SQL 语句中,所以存在 SQL 注入的风险。如果参数值来自不可控的用户输入,那么可能被注入恶意的 SQL 代码。 - 灵活性:尽管风险较高,
${}
在某些场景下是必要的。特别是在 SQL 语句的某些部分无法通过预编译实现,如动态改变表名或某些数据库函数和命令,${}
提供了必要的灵活性。
推荐文章和书籍
文章:https://zhuanlan.zhihu.com/p/86293659
书籍:《 Java 核心技术卷 I 》
欢迎交流
当讨论 MyBatis 中的 #{}
和 ${}
占位符时,我们可以从以下几个角度深入探讨它们的概念、区别和实际应用,以便更好地理解它们在数据库操作中的作用:
1)什么是 #{}
和 ${}
?它们在 MyBatis 中有什么应用?
2)#{}
和 ${}
的工作原理是什么?
3)#{}
和 ${}
在实际应用中的选择标准是什么?