Mybatis实战:#{} 和 ${}的使用区别和数据库连接池

一.#{} 和 ${}

#{} 和 ${} 在MyBatis框架中都是用于SQL语句中参数替换的标记,但它们在使用方式和处理参数值上存在一些显著的区别。

#{}的作用

  1. #{} 是MyBatis中用于预编译SQL语句的参数占位符。
  2. 它会将参数值放入一个预编译的PreparedStatement中,确保参数值被正确地转义和引用,从而防止SQL注入攻击。

特点: 

  • 预编译:数据库驱动在发送SQL语句和参数给DBMS之前对SQL语句进行编译,DBMS执行SQL时不需要重新编译,提高了执行效率。
  • 安全性:由于使用了预编译机制,可以有效防止SQL注入攻击。
  • 类型转换:自动进行Java类型和JDBC类型转换。
  • 适用性:适用于大多数情况,特别是当参数值是从用户输入中获得时,因为它提供了更好的安全性和可靠性。

${}的作用

  • ${} 是MyBatis中用于字符串拼接的参数标记。
  • 它会将参数值直接嵌入到SQL语句中,不进行预编译或转义。

特点

  • 字符串替换:纯粹的字符串替换,不进行预编译或转义,直接替换成变量的值。
  • 安全性风险:由于不进行预编译或转义,如果不正确地处理参数值,可能会导致SQL注入攻击。
  • 适用性:适用于一些特殊情况,如动态表名、列名或函数调用等,但需要谨慎使用,确保参数值的安全性。

 

1.1Interger类型的参数

1.先看Interger类型的参数
@Select("select username, `password`, age, gender, phone from userinfo where 
id= #{id} ")UserInfo queryById(Integer id);

2.观察日志

 3.查看日志中的输出语句

select username, `password`, age, gender, phone from userinfo where id= ?

 我们输⼊的参数并没有在后⾯拼接,id的值是使⽤ ? 进⾏占位. 这种SQL 我们称之为"预编译SQL"。

4.我们把 #{} 改成 ${} 再观察打印的⽇志:
@Select("select username, `password`, age, gender, phone from userinfo where 
id= ${id} ")UserInfo queryById(Integer id);

5.再次查看输出日志信息

可以看到, 这次的参数是直接拼接在SQL语句中了。

1.2 String类型的参数

1.传入String类型的参数

@Select("select username, `password`, age, gender, phone from userinfo where 
username= #{name} ")UserInfo queryByName(String name);

2.观察我们打印的⽇志, 结果正常返回

3.我们把 #{} 改成 ${} 再观察打印

@Select("select username, `password`, age, gender, phone from userinfo where 
username= ${name} ")UserInfo queryByName(String name);

4.查看日志 

 

可以看到, 这次的参数依然是直接拼接在SQL语句中了, 但是字符串作为参数时, 需要添加引号 '' , 使⽤ ${} 不会拼接引号 '' , 导致程序报错.
5.修改代码(加上' ')
@Select("select username, `password`, age, gender, phone from userinfo where 
username= '${name}' ")
UserInfo queryByName(String name);

6.查看日志


 我们在IDEA进行代码示范

 1.声明方法中区别

@Select("select * from userinfo where username = #{userName}")UserInfo getUserByName(String userName);@Select("select * from userinfo where username = ${userName}")UserInfo getUserByName2(String userName);

2.分别进行单元测试

#运行成功

 $运行失败

为什么呢?

因为#{} 使⽤的是预编译SQL, 通过 ? 占位的⽅式, 提前对SQL进⾏编译, 然后把参数填充到SQL语句中. #{} 会根据参数类型, ⾃动拼接引号 '' 。

${} 会直接进⾏字符替换, ⼀起对SQL进⾏编译. 如果参数为字符串, 需要加上引号 '' 。

 

运行成功



1.3#{} 和 ${}区别 

1.3.1.性能不同

#{} 和 ${} 的区别就是预编译SQL和即时SQL 的区别.。
当客⼾发送⼀条SQL语句给服务器后, ⼤致流程如下:
  1. 解析语法和语义, 校验SQL语句是否正确
  2. 优化SQL语句, 制定执⾏计划
  3. 执⾏并返回结果
⼀条 SQL如果⾛上述流程处理, 我们称之为 Immediate Statements(即时 SQL)。
绝⼤多数情况下, 某⼀条 SQL 语句可能会被反复调⽤执⾏, 或者每次执⾏的时候只有个别的值不同(⽐如 select 的 where ⼦句值不同, update 的 set ⼦句值不同, insert 的 values 值不同). 如果每次都需要 经过上⾯的语法解析, SQL优化、SQL编译等,则效率就明显不⾏了.
总之:#预编译SQl的性能更高,是提前占座的行为,$即时SQL是直接拼接的行为。 
预编译SQL,编译⼀次之后会将编译后的SQL语句缓存起来,后⾯再次执⾏这条语句时,不会再次编译。
(只是输⼊的参数不同), 省去了解析优化等过程, 以此来提⾼效率.

1.3.2SQL注⼊(面试点)

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

sql 注⼊代码: ' or 1='1 

1.先来看看SQL注⼊的例⼦:
@Select("select username, `password`, age, gender, phone from userinfo where 
username= '${name}' ")
List<UserInfo> queryByName(String name);
测试代码:
@Test
void queryByName() {List<UserInfo> userInfos = userInfoMapper.queryByName("admin");System.out.println(userInfos);
}

2.正常访问情况

3.SQL注⼊场景:

@Test
void queryByName() {
List<UserInfo> userInfos = userInfoMapper.queryByNamSystem.out.println(userInfos);
}

 结果依然被正确查询出来了, 其中参数 or被当做了SQL语句的⼀部分

4.依然正确运行

控制层: UserController
import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/login")public boolean login(String name, String password) {UserInfo userInfo = userService.queryUserByPassword(name, password);if (userInfo != null) {return true;}return false;}
}
业务层: UserService

import com.example.demo.mapper.UserInfoMapper;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {@Autowiredprivate UserInfoMapper userInfoMapper;public UserInfo queryUserByPassword(String name, String password) {List<UserInfo> userInfos = userInfoMapper.queryUserByPassword(name,password);if (userInfos != null && userInfos.size() > 0) {return userInfos.get(0);}return null;}
}
数据层: UserInfoMapper
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface UserInfoMapper {@Select("select username, `password`, age, gender, phone from userinfo where username= '${name}' and password='${password}' ")List<UserInfo> queryUserByPassword(String name, String password);
}
程序正常运⾏

接下来访问SQL注⼊的代码:

password 设置为 ' or 1='1


在IDEA运行示范

1.方法声明

 @Select("select * from userinfo where username = #{userName}")UserInfo selectUserByName(String userName);@Select("select * from userinfo where username = '${userName}'")UserInfo selectUserByName2(String userName);

2.单元测试

 

 @Testvoid selectUserByName() {System.out.println(userInfoMapper.selectUserByName("' or 1='1"));}@Testvoid selectUserByName2() {System.out.println(userInfoMapper.selectUserByName2("' or 1='1"));}

#运行:

$运行:

1.3.3排序功能

从上⾯的例⼦中, 可以得出结论: ${} 会有SQL注⼊的⻛险, 所以我们尽量使⽤#{}完成查询
既然如此, 是不是 ${} 就没有存在的必要性了呢?
当然不是.

 比如我进行id升序/降序时。

1.方法声明

使用#

@Select("select * from userinfo order by id #{order}")List<UserInfo> queryUserListByOrder(String order);

使用$

 

@Select("select * from userinfo order by id ${order}")List<UserInfo> queryUserListByOrder(String order);

 2.单元测试

#测试(失败)

可以发现, 当使⽤ #{sort} 查询时, asc 前后⾃动给加了引号, 导致 sql 错误
#{} 会根据参数类型判断是否拼接引号 '' 如果参数类型为String, 就会加上 引号

 $测试(成功)

使用场景:

使⽤ ${sort} 可以实现排序查询, ⽽使⽤ #{sort} 就不能实现排序查询了.
注意: 此处 sort 参数为String类型, 但是SQL语句中, 排序规则是不需要加引号 '' 的, 所以此时的${sort} 也不加引号

 除此之外, 还有表名作为参数时, 也只能使⽤ ${}。

1.3.4like 查询

LIKE进行模糊查询时,$能直接查询.

like 直接使⽤ #{} 报错

@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +"from userinfo where username like '%#{key}%' ")
List<UserInfo> queryAllUserByLike(String key);

把 #{} 改成 ${} 可以正确查出来, 但是${}存在SQL注⼊的问题, 所以不能直接使⽤ ${}.  

实现代码如下: 

@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +"from userinfo where username like concat('%',#{key},'%')")
List<UserInfo> queryAllUserByLike(String key);

总结

  1. #{}:预编译处理, ${}:字符直接替换
  2. #{} 可以防⽌SQL注⼊, ${}存在SQL注⼊的⻛险, 查询语句中, 可以使⽤ #{} ,推荐使⽤ #{}
  3. 但是⼀些场景, #{} 不能完成, ⽐如 排序功能, 表名, 字段名作为参数时, 这些情况需要使⽤${}
  4. 模糊查询虽然${}可以完成, 但因为存在SQL注⼊的问题,所以通常使⽤mysql内置函数concat来完成

二.数据库连接池

在上⾯Mybatis的讲解中, 我们使⽤了数据库连接池技术, 避免频繁的创建连接, 销毁连接
下⾯我们来了解下数据库连接池。
数据库连接池负责分配、管理和释放数据库连接,它允许应⽤程序重复使⽤⼀个现有的数据库连接,⽽不是再重新建⽴⼀个

 

  • 没有使⽤数据库连接池的情况: 每次执⾏SQL语句, 要先创建⼀个新的连接对象, 然后执⾏SQL语句, SQL 语句执⾏完, 再关闭连接对象释放资源. 这种重复的创建连接, 销毁连接⽐较消耗资源
  • 使⽤数据库连接池的情况: 程序启动时, 会在数据库连接池中创建⼀定数量的Connection对象, 当客⼾请求数据库连接池, 会从数据库连接池中获取Connection对象, 然后执⾏SQL, SQL语句执⾏完, 再把Connection归还给连接池.
优点 :
1. 减少了⽹络开销
2. 资源重⽤
3. 提升了系统的性能
常⻅的数据库连接池:
  • C3P0
  • DBCP
  • Druid
  • Hikari
⽬前⽐较流⾏的是 Hikari, Druid
1.Hikari : SpringBoot默认使⽤的数据库连接池

 Hikari 是⽇语"光"的意思(ひかり), Hikari也是以追求性能极致为⽬标

2. Druid

如果我们想把默认的数据库连接池切换为Druid数据库连接池, 只需要引⼊相关依赖即可

<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.17</version>
</dependency>

 运行结果:

Druid连接池是阿⾥巴巴开源的数据库连接池项⽬
功能强⼤,性能优秀,是Java语⾔最好的数据库连接池之⼀。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/389721.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java语言程序设计——篇十一(2)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

MySQL(8.0)数据库安装和初始化以及管理

1.MySQL下载安装和初始化 1.下载安装包 下载地址&#xff1a;https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.33-1.el7.x86_64.rpm-bundle.tar wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.33-1.el7.x86_64.rpm-bundle.tar 2.解压…

数据同步策略概览

数据同步在业务开发中比较普遍&#xff0c;例如 订阅MySQL的binlog将数据同步至异构数据库。数据同步方案需要考虑一下几点&#xff1a; 数据实时性要求数据量级是否有数据转换逻辑 可分为两种模式 发布订阅模式&#xff1a;分为订阅数据库log还是订阅应用层发的消息点对点模…

问界M7是不是换壳东风ix7? 这下有答案了

文 | AUTO芯 作者 | 谦行 终于真相大白了 黑子们出来挨打啊 问界M7是换壳的东风ix7&#xff1f; 你们没想到&#xff0c;余大嘴会亲自出来正面回应吧 瞧瞧黑子当时乐的 问界你可以啊&#xff01;靠改名字造车呢&#xff1f; 还有更过分的&#xff0c;说M7是东风小康ix7…

【网络】网络入门(第一篇)

网络入门可以从多个方面开始&#xff0c;以下是一个基本的网络入门指南&#xff0c;涵盖了网络的基本概念、网络类型、网络协议、网络拓扑、网络设备以及网络地址等方面。 一、网络基本概念 计算机网络&#xff1a;将多个计算机系统和设备连接在一起&#xff0c;以实现资源共…

CANoe系统变量模块里定义的结构体类型和变量从CAPL代码角度理解

CAPL里声明一个结构体类型&#xff1a; variables {struct DoIPMessage{byte version;byte inVersion;word type;dword length;byte payload[1500];};struct DoIPMessage doipMessage; }声明一个结构体类型DoIPMessage&#xff0c;定义了一个此结构体…

【数据结构】哈希表(散列表)

目录 1、unordered系列关联式容器 2、哈希概念 3、哈希函数 3.1 直接定址法 3.2 除留余数法 4、哈希冲突 4.1 闭散列(开放定址法) 4.1.1 线性探测 4.1.2 二次探测 4.1.3 线性探测代码实现 插入 搜索 删除 对于不可以取模的类型 4.2 开散列(哈希桶/拉链法) 插入…

【pyhton】Python中zip用法详细解析与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

在WordPress上启用reCAPTCHA的指南

随着网络安全问题的日益严重&#xff0c;网站管理员必须采取措施保护自己的网站免受恶意攻击。对于WordPress用户来说&#xff0c;可以通过启用谷歌的reCAPTCHA功能来增强网站的安全性。本文将介绍两种在WordPress上启用reCAPTCHA的方法&#xff1a;使用插件和手动添加代码。 一…

白盒测试基础与实践:Python示例及流程图设计

文章目录 前言一、白盒测试是什么&#xff1f;主要特点常用方法优点缺点 二、白盒测试常用技术语句覆盖判定覆盖条件覆盖判定/条件覆盖条件组合覆盖路径覆盖 三、程序流程图设计四、测试用例设计1. 基本路径法2. 语句覆盖3. 判断覆盖4. 条件覆盖5. 判断/条件覆盖6. 条件组合覆盖…

两个好消息,你先听哪个?

1.第五大数据、人工智能与软件工程国际研讨会&#xff08;ICBASE 2024)成功申请IEEE出版&#xff0c;上线IEEE官网&#xff0c;欢迎投稿参会&#xff01;&#xff01;&#xff01; &#x1f4e3;IEEE独立出版&#xff0c;设置优秀评选 &#x1f525;院士加盟&#xff0c;中外高…

一个私有化的中文笔记工具个人知识库,极空间Docker部署中文版『Trilium Notes』

一个私有化的中文笔记工具&个人知识库&#xff0c;极空间Docker部署中文版『Trilium Notes』 哈喽小伙伴们好&#xff0c;我是Stark-C~ 最近被很多小伙伴问到NAS上的笔记工具&#xff0c;虽说之前也出过Memos&#xff0c;刚开始用起来还不错&#xff0c;但是用了一段时间…

(vue)el-cascader级联选择器按勾选的顺序传值,摆脱层级约束

(vue)el-cascader级联选择器按勾选的顺序传值,摆脱层级约束 需求&#xff1a;按勾选的顺序给后端传值 难点&#xff1a;在 Element UI 的 el-cascader 组件中&#xff0c;默认的行为是根据数据的层级结构来显示选项&#xff0c;用户的选择也会基于这种层级结构&#xff0c;el-…

文件解析漏洞—IIS解析漏洞—IIS7.X

在IIS7.0和IIS7.5版本下也存在解析漏洞&#xff0c;在默认Fast-CGI开启状况下&#xff0c;在一个文件路径/xx.jpg后面加上/xx.php会将 “/xx.jpg/xx.php” 解析为 php 文件 利用条件 php.ini里的cgi.fix_pathinfo1 开启IIS7在Fast-CGI运行模式下 在 phpstudy2018 根目录创建…

红酒与夜晚:享受静谧的品酒时光

当夜幕低垂&#xff0c;星光点点&#xff0c;世界仿佛进入了一个宁静而神秘的领域。在这样的夜晚&#xff0c;与一瓶定制红酒洒派红酒&#xff08;Bold & Generous&#xff09;相伴&#xff0c;便是一场令人陶醉的品酒之旅&#xff0c;让人在静谧中感受生活的美好。 一、夜…

《BiFormer: Vision Transformer with Bi-Level Routing Attention》CVPR2023

摘要 这篇论文提出了一种新型的视觉Transformer&#xff0c;名为BiFormer&#xff0c;它采用了双层路由注意力&#xff08;Bi-Level Routing Attention, BRA&#xff09;机制。注意力机制是视觉变换器的核心构建模块&#xff0c;能够捕获数据中的长期依赖性。然而&#xff0c;…

java远程调试

java远程调试 idea2024创一个Spring Web项目springdemo1 使用maven-assembly-plugin插件打包成JAR文件 pom.xml参考如下 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi&quo…

离线安装MeterSphere遇到的问题

1.安装步骤&#xff0c;参考官方文档 在线安装 - MeterSphere 文档 2.安装完成以后&#xff0c;docker ps查看有很多服务一直处于重启状态&#xff0c;查看容器日志docker logs ID,发现所有一直处于重启状态的容器都是因为服务无法创建日志目录和文件。一直处于重启的服务如…

NAND行业回归盈利:AI与云存储需求驱动

市场概览 根据Yole Group于2024年6月25日发布的市场报告&#xff0c;经过五个季度的亏损之后&#xff0c;NAND闪存行业在2024年第一季度&#xff08;1Q24&#xff09;实现了盈利回归。这一转变主要得益于企业级固态硬盘&#xff08;SSD&#xff09;领域的强劲需求增长&#xf…

画图像解方程系列-FPI

不是所有方程都能求出精确解。 解方程 sinx(x) cos(x)&#xff0c;求x&#xff0c;在区间&#xff08;0&#xff0c;1&#xff09;范围内。 正常解法&#xff1a; 两边除以cosx得到tanx 1 解的x Π/4&#xff0c;使用计算机计算得到&#xff1a;0.7853981633974483096156…