Mybatis中#{}和${}到底有什么区别?

前言

我们在使用mybatis开发的过程中,经常会用到动态SQL,用到动态SQL就会用到占位符,$ {} 和 # {}是 mybatis中的两种占位符,其最大的区别就是 # {} 可以防止SQL注入,但是${}不可以 ,首先了解以下什么是SQL注入。

什么是SQL注入

SQL 注入是一种常见的安全漏洞,攻击者利用该漏洞可以通过构造特定的输入,向数据库中插入恶意代码或者修改数据库中的数据,甚至可以获取敏感数据。

SQL 注入通常发生在 Web 应用程序中,攻击者可以在用户输入的数据中注入 SQL 代码。当应用程序没有正确地过滤和转义用户输入的数据时,就会存在 SQL 注入的风险。例如,一个简单的用户登录页面,如果没有对用户输入的用户名和密码进行正确的过滤和转义,攻击者就可以在用户名或密码中注入 SQL 代码,例如:

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

上述代码中的 OR 1=1 表示条件永远成立,这样就可以绕过密码验证直接登录到系统中。

为了避免 SQL 注入攻击,开发人员应该对用户输入的数据进行正确的过滤和转义。在 MyBatis 中,可以使用 #{} 占位符来自动预编译处理传入参数,从而有效地防止 SQL 注入攻击。此外,还可以使用转义函数等方式来对用户输入的特殊字符进行处理,从而保证 SQL 语句的安全和可靠。

什么是预编译

预编译是指在程序执行 SQL 语句之前,数据库会对 SQL 语句进行编译和优化,生成一个可执行的执行计划,并缓存这个执行计划。这个执行计划包含了 SQL 语句的语法分析、表结构分析、索引分析等信息,能够在执行 SQL 语句时快速定位到数据的存储位置,从而提高查询的效率。

预编译就是将SQL语句中的值用一个占位符来进行替代,将SQL语句进行模板化,实现一次编译,多次运行。

预编译的过程一般在程序运行前执行,只需进行一次,不必每次执行 SQL 语句时都进行编译。这可以有效地减少数据库的运算量和网络传输的数据量,提高程序的执行效率。

Mybatis底层使用 PreparedStatement ,默认情况下,将对所有的 SQL进行预编译,将#{}替换为?,然后将带有占位符 ? 的SQL模板发送至MySQL服务器,由服务器对此无参数的SQL进行编译后,将编译结果缓 存,然后直接执行带有真实参数的SQL。

预编译的好处:

  1. 提高执行的效率:预编译将SQL语句和参数分离开来,将SQL语句编译成一个执行计划,参数只需要传递即可,不需要每次使用这个SQL语句的时候都要重新进行编译,减少了执行时间和资源的消耗,减少了数据库的负载。
  2. 提高安全性,防止SQL注入:预编译可以防止SQL注入,因为其将会无视参数进行编译,所以参数不会对SQL的编译结果产生任何的影响,从而避免了恶意代码的注入。
  3. 预编译语句可以重复利用:当预编译之后,会将其存入缓存中,当有相同的语句执行的时候,会直接使用缓存中的执行计划,提高了执行效率。

#{}的实现原理

#{} 占位符的使用非常简单,在 SQL 语句中可以使用 #{} 来代替需要传入的参数。每次#{}会被解析成预编译的语句,预编译之后可以直接执行,不需要重新编译SQL。例如,可以使用以下语法来查询用户表中的某个用户:

SELECT * FROM user WHERE username = #{username}

其会先预编译成:

SELECT * FROM user WHERE username = ?

其中,#{username} 会被 MyBatis 解析为一个占位符,表示从 Java 对象中获取 username 属性的值,并将该值转换为 SQL 语句中的参数。

在实际使用中,#{} 占位符还支持一些特殊的语法,例如:

  1. 支持类型转换:可以使用 #{} 将传入参数进行类型转换,例如 #{age,jdbcType=INTEGER} 表示将传入的 age 参数转换为 INTEGER 类型。
  2. 支持表达式:可以使用 #{} 来编写表达式,例如 #{age+1} 表示将传入的 age 参数加 1。
  3. 支持特殊字符转义:在 #{} 中使用转义字符 \,可以对特殊字符进行转义,例如 #{‘Tom’s book’} 表示将单引号进行转义,从而避免 SQL 注入攻击。

${}的实现原理

${}仅仅为一个字符串替换,它将占位符替换成一个文本字符串,然后生成SQL语句,在每次执行SQL的时候都会重新进行编译,有SQL注入的问题。

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

当传递的参数为"cuisenghe"的时候,其会将上面的语句解析成以下形式,然后给数据库进行编译,然后执行。

select * from user where username = "cuisenghe"

也就是说,当使用此种方式的时候,因为没有进行预编译,所以如果用户传来的参数也是一个SQL语句的话,其会拼接在之前的SQL语句后面,再进行编译的时候,会将其也视作SQL语句来进行执行,从而引发了SQL注入的风险。

总结

总的来说,${} 的使用应该尽量避免,只在需要动态拼接 SQL 语句时才使用,并且需要特别注意安全性问题。在普通的 SQL 语句中,建议使用 #{} 占位符,以提高 SQL 语句的可读性、可维护性和安全性。

欢迎大家访问我的个人博客!

博客地址:个人博客

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小崔同学24

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值