java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > MyBatis #{} 与 ${} 参传差异解码

MyBatis 探秘之#{} 与 ${} 参传差异解码(数据库连接池筑牢数据交互根基)

作者:GGBondlctrl

本文详细介绍了MyBatis中的`#{}`和`${}`的区别与使用场景,包括预编译SQL和即时SQL的区别、安全性问题,以及如何正确使用数据库连接池来提高性能,感兴趣的朋友一起看看吧

📚️1. #{} 与 ${}的使用

我们在之前的学习中,了解到了“#{}”,但是这里的${}是什么呢?其实这里的${}也具有参数传递的功能但是我们之前为什么不使用$符号呢?且听下面的分析过程~~~

1.1integer类型数据

我们可以通过id类整型参数的传递进行实验,首先得先创建一个数据库,如下所示:

接下来我们知己使用XML的方式进行代码的编写:

在Mapper类中:

 List<UserInfo> select2(Integer id);

 这里先定义数据的返回类型,然后再在XML文件中实现查询SQL的方法:

<select id="select2" resultType="com.example.mybatis.Model.UserInfo">
        select * from user_info where id=#{id}
    </select>

然后再测试类进行测试后们可以看到如下的打印的日志:

解释:

可以发现在SQL查询语句中,参数被“?”给替代了,然后下面的参数就是“2”,此时可以了解到我们输⼊的参数并没有在后⾯拼接,id的值是使⽤ ? 进⾏占位. 这种SQL 我们称之为"预编译SQL"

然后我们将这里的“#{ }” 替换成“${}”,具体的情况就是如下所示的:

解释:

可以发现此时SQL语句,就没有“?”,然后下面的paramters参数就为空了,然后我们就知道${} 会直接进⾏字符替换, ⼀起对SQL进⾏编译,不会像#号一样使用占位符;这种我们就称之为即时SQL

1.2String类型数据

这里我们使用String类来进行匹配查询,具体的操作还是和上面差不多;

Mapper类的代码如下:

 List<UserInfo> selectAllByUsername(String username);

然后再XML写SQL语句的操作:

解释:

此时可以看到,和上面的string类型的数据是一样的,这里会标明参数的类型为string类型,然后再进行替换占位的时候会自动添加引号;

然后我们将这里的“#{ }” 替换成“${}”,具体的情况就是如下所示的:

 <select id="selectAllByUsername" resultType="com.example.mybatis.Model.UserInfo">
        select * from user_info where username=${username}
    </select>

这里就是通过输入的名字进行查询,将这#号替换成了$符号,然后再测试类进行测试后打印的日志报错了,具体的打印日志如下所示:

解释:

这里可以看到此时的$符号的操作,出现了报错,原因就是BadSql,我们在上面看到,由于直接代替的原因,查询条件中字符串没有出现“ '  '  ”符号,即引号,然后就是SQL语句语法不正确导致错误;

解决办法:

在XML编写SQL语句的时候进行手动添加' ';

代码如下:

<select id="selectAllByUsername" resultType="com.example.mybatis.Model.UserInfo">
        select * from user_info where username='${username}'
    </select>

此时进行测试打印,打印的日志就是如下所示的:

解释:此时可以看到在字符串添加了“ ' ' ”双引号,然后参数仍然为空;

综上所述:

#{} 使⽤的是预编译SQL, 通过 ? 占位的⽅式, 提前对SQL进⾏编译, 然后把参数填充到SQL语句中. #{} 会根据参数类型, ⾃动拼接引号 '' .
${} 使用的就是即时编译SQL,会直接进⾏字符替换, ⼀起对SQL进⾏编译. 如果参数为字符串, 需要加上引号 ''

📚️2. #{} 与 ${}的区别

2.1性能

当客⼾发送⼀条SQL语句给服务器后, ⼤致流程如下:

1. 解析语法和语义, 校验SQL语句是否正确
2. 优化SQL语句, 制定执⾏计划
3. 执⾏并返回结果

⼀条 SQL如果⾛上述流程处理, 我们称之为 Immediate Statements(即时 SQL) 

但是绝⼤多数情况下, 某⼀条 SQL 语句可能会被反复调⽤执⾏, 或者每次执⾏的时候只有个别的值不同(⽐如 select 的 where ⼦句值不同, update 的 set ⼦句值不同, insert 的 values 值不同). 如果每次都需要经过上⾯的语法解析, SQL优化、SQL编译等,则效率就明显不⾏了

总结: 

所以预编译SQL就在执行上述的优化操作后,遇到同样的SQL语句,就不会对SQL进行再次的优化编译了,就直接改变参数,省去了解析优化等过程, 以此来提⾼效率

预编译SQL的性能比即时SQL的性能更高;

2.2安全性 

这里出现的安全性就是(SQL注入)

SQL注⼊:是通过操作输⼊的数据来修改事先定义好的SQL语句,以达到执⾏代码对服务器进⾏攻击的⽅法。由于没有对⽤⼾输⼊进⾏充分检查,⽽SQL⼜是拼接⽽成,在⽤⼾输⼊参数时,在参数中添加⼀些SQL关键字,达到改变SQL运⾏结果的⽬的,也可以完成恶意攻击

注意:这里只针对的就是${ }符号;

假如我们在此符号中添加    ' or 1='1

然后在#{ }运行中,可以发现此时的此时的打印日志告诉我们,这里的字符串在数据库中没有找到

如下所示:

这里的结果确实如我们所料,但是在${ }符号中,如下所示:

解释:

哎奇怪,怎么会出现这种情况,这个字符串在我们的数据库列中就没有这个字段;但是为啥就全部搜索出来了呢??

因为拼接的问题和原因,所以这里出现了误判,'  {'    or     1='1}  ' 拼接后成了以下SQL语句

'  '    or     1='1'

解释:

具体的意思就是为空或者为true,这就是为啥全部搜索出来了,这里存在SQL关键字or;

所以SQL注⼊是⼀种⾮常常⻅的数据库攻击⼿段, SQL注⼊漏洞也是⽹络世界中最普遍的漏洞之⼀.
如果发⽣在⽤⼾登录的场景中, 密码输⼊为 ' or 1='1 , 就可能完成登录;

总之在后面的学习中能用#{} 那么就使用#{ },${ }非特殊情况尽量不要使用 

📚️3.${ }使用场景

3.1排序

在实现排序功能的时候,由于“desc”不需要使用引号,所以这里我们就可以使用${}符号

具体的代码如下所示:

<select id="selectAllById" resultType="com.example.mybatis.Model.UserInfo">
        select * from user_info order by id ${sort}
    </select>

这里即时XML的写法,可以看到此时order by id然后排序关键词,就不需要引号,那么此时我们就可以使用$符号;

在测试类中的实现代码:

 @Test
    void selectAllById() {
        userInfoXMLMapper.selectAllById("desc");
    }

我们这里就是按照降序排序进行查询结果的排序的,最后打印的日志如下所示:

解释:

上面的两行就是SQL语句和参数,参数为空,然后即时SQL进行拼接,SQL语句就成为了一个查询语句按照降排序的方式进行查询结果的展示;

3.2模糊查询

代码如下所示:

 <select id="selectByUsername" resultType="com.example.mybatis.Model.UserInfo">
        select * from user_info where username like '%${username}%'
    </select>

这里也是不需要引号,然后也可以使用$符号,在测试类中代码:

@Test
    void selectByUsername() {
        userInfoXMLMapper.selectByUsername("o");
    }

最后打印的日志如下所示:

解释:

这里的模糊查询,中间的参数是不需要自动添加引号的,并且这里的模糊查询的条件就是查找名字里包含“o”的那一段数据;

但是这里由于注入等安全性,这里我们可以使用#进行另一种写法,具体的代码如下所示:

 <select id="selectByUsername2" resultType="com.example.mybatis.Model.UserInfo">
        select * from user_info where username like concat('%',#{username},'%')
    </select>

解释:

这里使用concat关键字,实现需要引号的拼接操作,这样就可以使用#{}来进行参数的传递,几避免了SQL注入的安全问题,还可能提高了执行的效率;

📚️4.数据库连接池

4.1介绍

数据库连接池负责分配、管理和释放数据库连接,它允许应⽤程序重复使⽤⼀个现有的数据库连接,⽽不是再重新建⽴⼀个

没有使⽤数据库连接池的情况: 每次执⾏SQL语句, 要先创建⼀个新的连接对象, 然后执⾏SQL语句, SQL语句执⾏完, 再关闭连接对象释放资源. 这种重复的创建连接, 销毁连接⽐较消耗资源

使⽤数据库连接池的情况: 程序启动时, 会在数据库连接池中创建⼀定数量的Connection对象, 当客⼾请求数据库连接池, 会从数据库连接池中获取Connection对象, 然后执⾏SQL, SQL语句执⾏完, 再把Connection归还给连接池 

优点:

1. 减少了⽹络开销
2. 资源重⽤
3. 提升了系统的性能 

4.2使用

常⻅的数据库连接池:

• C3P0
• DBCP
• Druid
• Hikari

主流就是Hikari,Druid;

我们的SpringBoot默认使用的就是Hikari;日志如下所示:

可以看到这就是springboot默认使用的就是Hikari;

📚️5.总结

本期小编主要讲解了关于#{ },${ },的区别与如何进行使用,讲解了两者的性能比较,比较重要的SQL注入的问题,以及简单的阐述了数据库连接池的介绍~~~

到此这篇关于MyBatis 探秘:#{} 与 ${} 参传差异解码,数据库连接池筑牢数据交互根基的文章就介绍到这了,更多相关MyBatis #{} 与 ${} 参传差异解码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文