浅谈一下MyBatis的#{param}和${param}

浅谈一下MyBatis的#{param}和${param}

我们都知道,MyBatis是一个很灵活的ORM框架,这其中就离不开#{param}和${param}的使用。但是本文就不谈什么是ORM框架了,大家可以自己了解一下。

但是在讲#{param}和${param}和以前,我要先来谈谈Mysql的预编译技术,我们都知道,当我们的SQL语句发送给MySQL服务器后,MySQL会对我们的SQL语句进行校验、解析等操作。但是很多情况下,我们的SQL语句都是相同的,只是参数有所不同,如果每次都要进行一遍校验、解析等操作,无疑是十分浪费性能的,于是就有了预编译技术。

MySQL 的预编译机制
1. 预编译过程:
  • 当客户端发送一个带有占位符(?)的 SQL 语句(使用 PreparedStatement)给 MySQL 时,MySQL 首先会对该 SQL 语句进行解析、语法检查、优化,然后将其编译为一个执行计划。
  • 这个过程称为“预编译”。之后,预编译的 SQL 语句会被存储在 MySQL 的内部缓存中。
2. 如何缓存和识别已预编译的语句:
  • MySQL 通过维护一个预编译语句缓存来存储已预编译的语句。这个缓存是一个内存结构,用于保存已经预编译过的 SQL 语句及其相应的执行计划。
  • 当一个 SQL 语句(使用占位符的 PreparedStatement)再次执行时,MySQL 会检查这个缓存,看看该 SQL 语句是否已经存在于缓存中。
  • MySQL 根据 SQL 语句的文本和参数结构(不包括具体的参数值)生成一个哈希值。然后,它使用这个哈希值在缓存中查找是否已经存在对应的预编译语句。
3. 缓存命中和重用:
  • 如果缓存中存在该 SQL 的执行计划,称之为“缓存命中”,MySQL 将直接使用缓存中的执行计划,而无需重新解析和编译。这样就大大提高了性能。
  • 如果缓存中没有找到该 SQL 语句的执行计划(“缓存未命中”),MySQL 就需要重新解析和编译该 SQL 语句,然后将新的执行计划存入缓存中。

所以,在有了预编译技术后可以很大的提升SQL语句的执行性能。

MyBatis中#{param}和${param}的区别
  • #{param}(占位符绑定)
    • #{param} 是参数占位符,它会被 MyBatis 转换为使用 PreparedStatement? 占位符,并在执行 SQL 时将参数值绑定到这些占位符。
    • 使用 #{param} 会启用数据库的预编译机制,因为参数的值是在执行时由数据库使用绑定变量进行替换的。
    • 优点:这种方式能够防止 SQL 注入,同时利用数据库的预编译机制提高性能。
  • ${param}(字符串替换)
    • ${param} 会直接将参数的值插入到 SQL 语句中,像字符串拼接一样。这种方式会在 SQL 拼接时将参数的值直接插入到查询中,生成的 SQL 语句不再含有占位符。
    • 由于 ${param} 是直接将值插入到 SQL 语句中,因此它不会使用数据库的预编译机制
    • 风险:这种方式有 SQL 注入的风险,因为参数的值可能包含恶意的 SQL 代码。

所以,也就是说,在使用#{param}的地方,会将对应位置的参数作为一个整体替换掉对应位置的占位符'?'怎么理解这句话呢,比如说下面这条SQL语句

select user_name from user where id = #{userId}

由于使用的是#{},所以会预编译为

select user_name from user where id = ?

如果对应的参数是"1"(1才是参数内容,外面的""只是表示这是一个字符串),那么这条语句最终会变成

select user_name from user where id = '1'

又或者如果对应的参数是 "1;DROP TABLE user;"最终这条语句会变成

select user_name from user where id = '1;DROP TABLE user;'

也就是说,用户输入的参数,会作为一个整体替换掉原来的占位符,其实就是会把用户输入的参数用单引号包成一个整体传进去。

但是如果使用的是${},最终这条语句会变成

select user_name from user where id = 1;DROP TABLE user;

使用${}时会直接将内容拼接到对应的位置,如果是上面这种情况,最终user表会被删除,这其实就是我们所说的SQL注入的风险。

但是,有的情况又只能使用${},比如:

select * from ${tableName} where id = #{id}

假设tableName="user",id="1",那么上面的语句会变为

select * from user where id = '1'

但是如果是#{tableName}

select * from #{tableName} where id = #{id}

最终会变成下面的情况,不能提供MySQL的语法校验,最终会报错。

select * from 'user' where id = '1'

其实我们在使用的时候只要记住,使用#{}时会在参数的整体加上单引号,然后思考一下什么情况加上单引号会出错就好了,然后就是能用#{}的地方尽量都用#{},可以防止SQL注入,同时也会使用到预编译在性能上来说也会比${}更好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值