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

news2024/9/22 19:42:29

一.#{} 和 ${}

#{} 和 ${} 在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.queryByNam
 System.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 {
    @Autowired
    private 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 {
    @Autowired
    private 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.单元测试

 

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

    @Test
    void 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.coloradmin.cn/o/1973904.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript ES6语法详解(下)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;我是码喽的自我修养&#xff01;今天给大家分享JavaScript ES6语法详解(下)&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到带大家&#xff0c;欢迎收藏关注…

信创企业级即时通讯发展趋势,私有化安全沟通

信创&#xff08;创新型科技公司&#xff09;在当今的商业环境中发挥着重要作用&#xff0c;因此&#xff0c;他们对于私有化安全沟通的需求日益增加。企业级即时通讯软件是为满足企业内部沟通和协作需求而设计的重要工具。在信创企业中&#xff0c;采用私有化安全沟通解决方案…

乐乐音乐Kotlin版

简介 乐乐音乐Kotlin版&#xff0c;主要是基于ExoPlayer框架开发的Android音乐播放器&#xff0c;它支持lrc歌词和动感歌词(ksc歌词、krc歌词、trc歌词、zrce歌词和hrc歌词等)、多种格式歌词转换器及制作动感歌词、翻译歌词和音译歌词。 编译环境 Android Studio Jellyfish | …

计算机毕业设计Python+Tensorflow股票推荐系统 股票预测系统 股票可视化 股票数据分析 量化交易系统 股票爬虫 股票K线图 大数据毕业设计 AI

1、用pycharm打开项目&#xff0c;一定要打开包含manage.py文件所在文件夹 2、配置解释器&#xff1a;建议使用Anaconda(Python 3.8(base))&#xff0c;低于3.8版本的&#xff0c;页面会不兼容 3、安装依赖库&#xff1a;打开pycharm的终端&#xff0c;输入&#xff1a; pip in…

第TR3周:Pytorch复现Transformer

本文为365天深度学习训练营 中的学习记录博客 原作者&#xff1a;K同学啊 任务详情&#xff1a; ●1. 从整体上把握Transformer模型&#xff0c;明白它是个什么东西&#xff0c;可以干嘛 ●2. 读懂Transformer的复现代码&#xff08;暂时不要过于纠结于某一个点&#xff0c;后面…

重生之我们在ES顶端相遇第9 章- 搜索框最常用的功能 - 搜索建议

文章目录 1 前言2 Term Suggester2.1 基本介绍2.2 使用 demo2.3 常用参数2.3.1 suggest_mode2.3.2 max_edits2.3.3 prefix_length2.3.4 min_word_length 3 Completion Suggester3.1 基本描述3.2 基本使用3.3 查询参数3.3.1 size3.3.2 skip_duplicates3.3.3 fuzzy queries(模糊查…

【WPF开发】安装环境、新建工程

一、安装环境 在安装VS时候&#xff0c;勾选安装开发环境 如果已安装VS&#xff0c;可以到工具中查看是否有相应环境 二、新建工程 点击“创建新项目” 通过顶部过滤&#xff0c;C#&#xff0c;选择“WPF应用&#xff08;NET.framework&#xff09;”&#xff0c;并点击“下一…

通过 ACM 论文模版学习 LaTeX 语法 【三、格式】

文章目录 一、LaTeX 简介二、ACM 论文模版三、格式3.1 文章格式3.1.1 注释3.1.2 空格3.1.3 换行 3.2 字体3.2.1 字体样式3.2.2 字体大小2.2.3 字体颜色 一、LaTeX 简介 通过 ACM 论文模版学习 LaTeX 语法 【一、LaTeX简介和安装】 二、ACM 论文模版 通过 ACM 论文模版学习 L…

一款免费开源绿色免安装的透明锁屏工具

一款免费开源绿色免安装的透明锁屏工具 这个工具的特点就是电脑锁屏的时候&#xff0c;仍然显示原桌面&#xff0c;但是无法操作&#xff0c;需要输入密码才可以解锁。输入密码界面也是隐藏的需要按键才能显示输入密码框。 电脑★★★★★透明锁屏工具&#xff1a;https://pa…

canvas-视频绘制

通过Canvas元素来实时绘制一个视频帧&#xff0c;并在视频帧上叠加一个图片的功能可以当作水印。 获取Canvas元素&#xff1a; let canvas document.getElementById(canvas) 通过getElementById函数获取页面中ID为canvas的Canvas元素&#xff0c;并将其存储在变量canvas中。 …

快速将网站从HTTP升级为HTTPS

在当今数字化的世界中&#xff0c;网络安全变的越来越重要&#xff0c;HTTPS&#xff08;超文本传输安全协议&#xff09;不仅能够提供加密的数据传输&#xff0c;还能增强用户信任度&#xff0c;提升搜索引擎排名&#xff0c;为网站带来多重益处。所以将网站从HTTP升级到HTTPS…

达利欧对话施一公:如何应对快速变化的世界?

本篇是对达利欧对话施一公&#xff1a;如何应对快速变化的世界&#xff1f;&#xff5c;凤凰《封面》这一视频的翻译与整理, 过程中为符合中文惯用表达有适当删改, 版权归原作者所有. 达利欧&#xff1a;我很兴奋&#xff0c;施教授和我有很多共同点&#xff0c;即使我们来自不…

DynamicDataSource多数据源的管理,动态新增切换数据源

文章目录 多数据源管理单数据源项目父工程版本与依赖yml配置文件实体类新增与修改时间MapperServiceController主启动类测试类 多数据源初始版yml配置文件配置类创建一个AbstractRoutingDataSourceController层测试 DynamicDataSource版本引入依赖yml配置文件Controller层Servi…

量化(Quantization)技术在实现边缘设备智能化中的关键作用

节选自论文《Edge AI: Quantization as the Key to On-Device Smartness》的第三节&#xff0c;由YN Dwith Chenna撰写&#xff0c;发表在2023年8月的《International Journal of Artificial Intelligence & Applications》上。论文主要探讨了边缘人工智能&#xff08;Edge…

thinkphp之命令执行漏洞复现

实战&#xff1a; fofa搜索thinkphp-- 第一步&#xff1a;先在dns平台上&#xff0c;点击Get SubDomain &#xff0c;监控我们的注入效果 返回dnslog查看到了Java的版本信息 打开kali监听端口 进行base64编码 bash -i >& /dev/tcp/192.168.189.150/8080 0>&1 …

【速解焦虑秘籍】5步助你轻松走出焦虑迷雾,拥抱自在生活!

在这个快节奏、高压力的时代&#xff0c;焦虑仿佛成了许多人难以摆脱的“隐形伴侣”。它悄无声息地侵入我们的生活&#xff0c;影响着我们的情绪、工作乃至人际关系。但别担心&#xff0c;今天就带你揭秘“走出焦虑症最快的方法”&#xff0c;通过以下五个实用步骤&#xff0c;…

地理科学专业| 中国大学排行榜(2024年)

地理科学专业| 中国大学排行榜&#xff08;2024年&#xff09; 原文链接

拓扑未来数据中台解决方案

概述 传统自动化控制往往聚焦于局部或模块&#xff0c;整体运作状态靠人工管理。缺乏从时间维度观察生产周期的手段&#xff0c;由于生产数据缺失&#xff0c;导致生产过程不透明&#xff0c;过程质量无记录&#xff0c;生产工艺难优化&#xff0c;生产效率难以提升。利用先进…

亚马逊F控期间,如何巧妙运用自养账号进行评价?

亚马逊每年都会经历一段风险控制周期&#xff0c;这一时期往往伴随着商品评价的删除和卖家账户的封禁&#xff0c;对此&#xff0c;经验丰富的卖家们已经习以为常。虽然表面上看是风险控制&#xff0c;但实际上亚马逊只是对消费者的购买行为进行记录。导致账户被封和评论被删除…