MyBatis操作数据库(3)

news2025/2/6 5:53:57

其它查询操作

#{}和${}

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);

测试代码:

正常访问情况:

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

结果运行正常: 

 SQL注入场景

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

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

相关文章

OSI七层网络攻击行为及防范手段

2020年3月3日&#xff0c;360安全大脑披露美国中央情报局攻击组织&#xff08;APT-C-39&#xff09;对我国大型互联网公司、政府部门及相关企业进行长达11年的网络攻击渗透&#xff0c;该组织所使用的网络武器和CIA“Vault7”项目中的网络武器完全吻合。如今随着互联网技术的蓬…

RocketMQ 事件驱动:云时代的事件驱动有啥不同?

作者&#xff1a;林清山&#xff08;隆基&#xff09; 前言&#xff1a; 从初代开源消息队列崛起&#xff0c;到 PC 互联网、移动互联网爆发式发展&#xff0c;再到如今 IoT、云计算、云原生引领了新的技术趋势&#xff0c;消息中间件的发展已经走过了 30 多个年头。 目前&a…

图片懒加载的三种方式

方法一:滚动监听 + scrollTop + offsetTop + innerHeight scrollTop:指网页元素被滚动条卷去的部分。 offsetTop:元素相对父元素的位置 innerHeight:当前浏览器窗口的大小。需要注意兼容性问题。 <!DOCTYPE html> <html lang="en"><head>&…

IDEA: Unable to resolve table ‘xxx‘

描述&#xff1a; 在 IDEA 连接到数据库后&#xff0c;SQL 语句提示 Unable to resolve table 表名&#xff0c;且其它字段也飘红报错。 解决&#xff1a; 右键点击数据库&#xff0c;选择 Tools -> Manage Shown Schemas... 勾选你所使用的数据库即可&#xff1a; 1、2、3…

软考 系统架构设计师系列知识点之大数据设计理论与实践(6)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之大数据设计理论与实践&#xff08;5&#xff09; 所属章节&#xff1a; 第19章. 大数据架构设计理论与实践 第3节 Lambda架构 本文部分内容参考&#xff1a; Lambda架构 - 简书 特此致谢&#xff01; 19.3.3 Lambda…

【Ansible自动化运维】Ansible入门基础信息【安装配置、常用命令与模块】

介绍安装配置注意事项yum安装验证安装配置host配置主机清单配置主控端被控端 常用模块命令组成command模块shell模块copy模块script模块 日志信息最后 介绍 Ansible 是一个开源 IT 自动化引擎&#xff0c;可自动执行供应、配置管理、应用程序部署、编排和许多其他 IT 流程。它可…

深入浅出学习切片LOD——ArcGIS server模拟缓存切片(影像快显)

一、第一次实践 原理 免切片实现影像服务的模拟切片&#xff0c;主要原理是接收前端传过来的xyz(行列层级)以及切片方案&#xff0c;计算出该请求的切片的四至经纬度信息&#xff0c;通过mapserver的exportImage接口&#xff0c;传入每个模拟切片的四至经纬度信息得到图片返回…

小程序视频下载器

下载高手&#xff0c;让小程序视频下载变得前所未有的简单&#xff01;专为非编程专业人士设计&#xff0c;该工具免去了繁琐的抓包软件学习过程&#xff0c;无需深入研究Fiddler或Charles的配置。它优化了视频、图片和音频资源的下载&#xff0c;提供直观的操作界面&#xff0…

大世界基尼斯见证辉煌,云仓酒庄首届酒类培训新高度诞生

近日&#xff0c;一场规模盛大的酒类培训盛会&#xff0c;在云仓酒庄的精心组织下圆满落幕。此次培训活动以其卓着的成果和盛大的规模&#xff0c;创下了大世界基尼斯纪录&#xff0c;为酒类培训领域树立了新的标杆。这一成就的取得&#xff0c;背后是云仓酒庄团队无数的心血与…

修改taro-ui-vue3的tabs组件源码增加数字标签

需求&#xff1a;taro-ui-vue3的tabs组件上增加数字标记 步骤一&#xff1a;node_modules文件夹下找到taro-ui-vue3/lib/tabs/index.js 把173行的这一段替换成下面这段&#xff0c;然后写上样式 default: () > item.number ? [h(View, {class: at-tabs__item_in}, {defau…

linux应急响应基础命令

一、cpu使用率-top top -c -o %CPU -c 显示进程的命令行参数 -o 按照CPU占用从大到小排序二、用户信息 1、查看系统所有用户信息 [rootcentos7 ~]# cat /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nol…

有道词典网页版接口分析与爬虫研究

说明&#xff1a;仅供学习使用&#xff0c;请勿用于非法用途&#xff0c;若有侵权&#xff0c;请联系博主删除 作者&#xff1a;zhu6201976 一、目标站点 有道词典网页版&#xff1a;网易有道 二、目标接口 url&#xff1a;https://dict.youdao.com/jsonapi_s?doctypejson&…

JUC专题——Java并发机制的底层实现原理

本文部分内容节选自《Java并发编程的艺术》 volatile 的应用 volatile 是轻量级的 synchronized, 它在多处理器开发中保证了共享变量的 “可见性”. 可见性的意思是当一个线程修改一个共享变量时, 另外一个线程能读到这个修改的值. 如果 volatile变量修饰符使用恰当的话, 它比…

面试算法-173-二叉树的直径

题目 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,…

linux的线程概念

目录 1.原理 2.线程的周边概念 3.创建线程的接口 1.pthread_create 2.pthread_join 3.pthread_detach 4.终止线程 5.C11封装的多线程库 4.线程库的大概结构 5.__thread&#xff08;只能修饰内置类型&#xff09; 6.线程的互斥 1.了解原理 2.加锁 1.接口 2.代码示…

护网 | 如何从蓝队初级进化到蓝队中级

了解应急响应的流程 1&#xff09;首先判断服务器资产、影响范围以及严重程度&#xff0c;确认有没有必要将服务器下线隔离&#xff0c;然后根据服务器的失陷时间和态势感知的告警&#xff0c;判断是由什么漏洞进来的 2&#xff09;其次就是取证排查阶段&#xff0c;如果是w…

get_program_dir() Ai回答是一个函数,用于获取当前程序.exe的目录。

#include <iostream> #include <filesystem>std::string get_program_dir() {return std::filesystem::current_path().string(); }int main() {// 调用函数获取当前程序的目录std::string program_dir get_program_dir();std::cout << "当前程序的目录…

认识一下RAG

1.RAG技术背景与挑战 2.RAG的核心概念 3.RAG的工作流程与架构 4.RAG的优化方法 RAG的提出 •Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks是一篇重要的论文(2020年5月) •REALM: Retrieval-Augmented Language Model Pre-Training (2020)就将BERT预训练模…

Go微服务: 服务限流原理, 负载均衡与API网关

微服务里面的限流 (uber/limit)概述 go 微服务保稳三剑客: 熔断&#xff0c;限流&#xff0c;负载均衡限流的作用 限制流量&#xff0c;在服务端生效 注意&#xff1a;熔断是客户端生效 保护后端服务 餐厅吃饭排队的问题&#xff0c;提供凳子&#xff0c;让等候&#xff0c;这就…

创建k8s deploy yaml文件的imagePullSecrets语句

镜像仓库是harbor kubectl create secret docker-registry key --docker-server192.168.0.190 --docker-usernameadmin --docker-passwordHarbor12345