MyBatis操作数据库(3)

其它查询操作

#{}和${}

MyBatis参数赋值有两种方式, 咱们前面使用了#{}进行赋值, 接下来来看两者的区别:

#{}和${}的使用

1.先看Integer类型的参数:

@Select("select username, password, age, gender, phone from userinfo where id = #{id}")
UserInfo queryById(Integer id);

我们观察一下打印的日志:

 

 我们发现输输入的参数并没有在后面拼接, id使用的是 ? 进行占位. 这种SQL我们称之为"预编译SQL".

我们把#{}换为${}再观察打印的日志:

@Select("select username, password, age, gender, phone from userinfo where id = ${id}")
UserInfo queryById2(Integer id);

 

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

2.接下来我们再看String类型的参数:

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

观察打印的日志, 发现正常返回. 

 

我们把#{}改为${}再观察打印的日志:

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

可以看到, 这次的参数依然是直接拼接在SQL语句中了, 但是字符串作为参数时, 需要添加引号 ' ', 使用${}而不添加引号, 会导致程序报错. 

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

再次运行, 结果正常返回:

从上面两个栗子可以看出:

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

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

参数为数字类型时, 也可以加上, 查询结果不变, 但是可能导致索引失效, 性能下降. 

#{}和${}区别

简单回顾:

当客户发送一条SQL语句给服务器后, 大致流程如下:

1.解析语法和语义, 校验SQL语句是否正确.

2.优化SQL语句, 指定执行计划.

3.执行并返回结果

 一条SQL语句如果走上述流程, 我们称之为即时SQL.

1.性能更高

绝大多数情况下, 某一条SQL语句可能会被反复调用执行, 或者每次执行的时候只有个别的值不同(比如select的where子句值不同, update的set子句值不同, insert的values值不同). 如果每次都需要经过上面的语法解析, SQL优化, SQL编译等, 则效率明显就不行了.

 

 预编译SQL, 编译一次之后会将会将编译后的SQL语句缓存起来, 后面再执行这条语句时, 不会再次编译(只是输入的参数不同), 省去了解析优化的过程, 一次提高效率.

2.更安全(防止SQL注入)

SQL注入: 是通过操作输入的数据来修改事先定义好的SQL语句, 以达到执行代码对服务器进行攻击的方法.

由于没有对用户输入进行充分检查, 而SQL又是拼接而成, 在用户输入参数时, 在参数中添加一些SQL关键字, 达到改变SQL运行结果的目的, 也可以完成恶意攻击.

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

先来看看SQL注入的栗子:

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

测试代码:

正常访问情况:

    @Testvoid queryByName() {List<UserInfo> userInfos = userInfoMapper.queryByName("admin");System.out.println(userInfos);}

结果运行正常: 

 SQL注入场景

    @Testvoid queryByName() {List<UserInfo> userInfos = userInfoMapper.queryByName("' or 1 = '1");System.out.println(userInfos);}

结果依然查询出来了, 其中参数or被当作了SQL语句的一部分.

 可以看出来, 查询的数据并不是自己想要的数据. 所以用于查询的字段, 尽量使用#{}预查询方式.

SQL注入是一种非常常见的数据库攻击手段, SQL注入漏洞也是网络世界中最普遍的漏洞之一. 如果发生在用户登录的场景中, 密码输入为 ' or 1 = '1, 就可能完成登录(不是一定会发生的场景, 需要看登录代码咋写). 

 排序功能

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

当然不是.

接下来我们来看一下${}的使用场景:

Mapper实现

@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time from userinfo order by id ${sort}")
List<UserInfo> queryAllUserBySort(String sort);

使用${sort}可以实现排序查询, 而使用#{sort}就不能实现排序查询了.

注意: 此处sort参数为String类型, 但是SQL语句中, 排序规则是不需要加引号 ' ' 的, 所以此时的${sort}也不加引号. 

我们把${}改成#{}

@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time from userinfo order by id #{sort}")
List<UserInfo> queryAllUserBySort(String sort);

运行结果: 

 可以发现, 当使用#{sort}查询时, asc前后自动给加了引号, 导致sql错误.

#{}会根据参数类型判断是否拼接引号 ' '

如果参数类型为String, 就会加上引号.

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

其实, 这样直接使用${}还是有一定风险的, 但是其实这无非就升序/降序两种情况. 我们可以直接写两个接口, 一个专门传"asc"以表示升序, 一个专门传"desc"以表示降序.

like查询

like使用#{}报错.

 @Select("select * from userinfo where username like '%#{key}%'")
List<UserInfo> queryAllUserByLike(String key);

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

解决方法: 使用mysql的内置函数concat()来处理, 实现代码如下:

 @Select("select * from userinfo where username like concat('%', #{key}, '%')")
List<UserInfo> queryAllUserByLike(String key);

总结: #{}和${}区别

1.#{}: 预编译处理, ${}:直接字符替换

2.#{}可以防止SQL注入, ${}存在SQL注入的风险, 查询语句中, 可以使用#{}, 推荐使用#{}

3.但是一些场景, #{}不能完成, 比如排序功能, 表名, 字段名作为参数时, 这些情况需要使用${}

4.以上场景可以有更安全的方式替换${}. 

数据库连接池

在上面Mybatis讲解中, 我们使用了数据库连接池技术, 避免频繁地创建连接, 销毁连接, 下面我们来了解一下数据库连接池:

介绍

数据库连接池负责分配, 管理和释放数据库连接, 它允许应用程序重复使用一个现有的数据库连接, 而不是重新建立一个.

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

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

优点:

1.减小了网络开销

2.资源重用

3.提升了系统性能. 

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

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

相关文章

护眼灯哪个品牌好?五大护眼灯品牌推荐

护眼灯哪个品牌好&#xff1f;在挑选对眼睛有益的台灯时&#xff0c;专业护眼台灯无疑是明智的选择。这类台灯不仅严格按照国家的标准制造&#xff0c;确保了安全与质量的可靠性&#xff0c;除此之外护眼台灯还采用了经过精心设计的发光结构&#xff0c;有效减少了光线对眼睛的…

MinIO + Prometheus + Grafana docker部署

文章目录 说明MinIO简介MinIO 容器化部署Prometheus服务地址配置方法一&#xff1a;先部署后修改方法二&#xff1a;部署时修改compose文件&#xff08;未验证&#xff09; MinIO Access Key配置Prometheus 容器化部署MinIO 生成抓取配置修改Prometheus配置文件Grafana 容器化部…

【Unity】游戏场景添加后处理特效PostProcessing

添加后处理特效PostProcessing 添加雾效果后处理何为后处理&#xff1f;添加后处理特效 添加雾效果 依次点击Window -> Rendering -> Lighting添加Lighting面板。 点击Lighting里面的Environment&#xff0c;找到Other Setting 将Fog选项勾选 更改下方的颜色 调整雾的浓…

Python教程:备份你的文件夹里面的数据

1.完全备份是最基本的备份类型&#xff0c;它涉及复制所有选定的数据到备份位置。无论文件是否自上次备份以来发生了变化&#xff0c;所有文件都会被复制。这种备份方式简单直接&#xff0c;确保了备份存储的数据总是最新的。 完全备份是通过递归复制源文件夹中的所有文件和子…

面试算法-175-将有序数组转换为二叉搜索树

题目 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被视…

链表里面头节点存在的目的

头节点存在的目的&#xff1a; 在单链表的使用中&#xff0c;头结点&#xff08;Header Node&#xff09;是一个常用的概念&#xff0c;特别是在进行链表操作时。头结点不是数据域中实际存储的数据节点&#xff0c;而是作为链表操作的辅助节点&#xff0c;它包含对第一个实际数…

面试经典150题——二叉树的最大深度

1. 题目描述 ​ 2. 题目分析与解析 这个题目有过一定基础的都应该知道&#xff0c;采用递归解决问题&#xff0c;因为要求一个二叉树的深度&#xff08;也就是高度&#xff09;&#xff0c;其实上就是根节点的左子树和右子树中高度最高的那个。因此这个问题就可以拆解为&…

【电控笔记6.1】稳定度判断

简要概括 现控:远离虚轴,稳定度越高 自控:相位裕度PM 增益裕度GM 开环传函 不稳定条件判断

微信小程序生成链接或二维码的对比

二维码 1.小程序样式码 获取参数直接options.xxx&#xff08;参数名&#xff09; 方法&#xff1a;微信公众平台 》工具》生成小程序二维码&#xff1b; 样式图&#xff1a;就一看就是小程序的二维码&#xff1b; 2.正方形二维码/链接 方法&#xff1a;微信公众平台》开…

[linux]进程控制——进程终止

一、main函数的返回值 我们在编写C语言的程序时&#xff0c;通常会这样写&#xff1a; int main() {return 0; } 那么我们为什么要返回&#xff08;return&#xff09;0 呢&#xff1f; 其实&#xff0c;main函数也是一个函数&#xff0c;它也会被调用&#xff0c;所以谁调…

面向对象设计模式之概念

一、面向对象设计模式 按目的分为创建型&#xff08;creational&#xff09;、结构型&#xff08;structural&#xff09;和行为型&#xff08;behavioural&#xff09;&#xff1b;按范围分为类模式和对象模式。 二、设计原则 两大基础设计原则 程序设计的原则&#xff1a;模…

stm32实现hid鼠标

启动CubelMX 选择芯片&#xff08;直接输入stm32f103zet6) 设置时钟 如下图 usb设置 配置usb设备 调试端口设置 配置时钟 项目输出设置 打开工程&#xff08;后记&#xff1a;此工程含有中文不能编译通过) 配置项目 配置调试器 编译无法通过 删除路径中的中文&#xff0c;以及…