预编译SQL

预编译SQL

预编译SQL是指在数据库应用程序 中,SQL语句在执行之前 已经通过某种机制(如预编译器)进行了解析、优化和准备,使得实际执行时可以直接使用优化后的执行计划,而不需要每次都重新解析和编译。这么说可能有一些抽象,那么让我们结合具体的代码进行讲解。

我们先来看两个JDBC代码,这是第一个:

java 复制代码
String sql = "select id, username, password, name, age from user where username = 'liubei' and password = '123456' "
statement.executeQuery(sql);

这个代码完成了一个DQL语句,可以从user表中查找usernameliubeipassword123456的用户。看起来这个SQL语句和代码都十分简单。因为这个代码中的SQL语句是一个静态SQL(参数硬编码 ,直接将查询的条件硬编码在了SQL语句中,可以直接使用Statement类发起查询。

我们来看第二个代码:

java 复制代码
// 编写SQL语句
String sql = "select id, username, password, name, age from user where username = ? and password = ?";
// 使用PreparedStatement对象创建预编译SQL
PreparedStatement statement = connection.prepareStatement(sql);
// 预编译SQL的两个参数都是String类型的,所以说使用setString方法补全其参数
statement.setString(1, "liubei");
statement.setString(2, "123456");
// 调用executeQuery方法发起一条DQL语句,并将查询结果封装到ResultSet类中
ResultSet resultSet = statement.executeQuery();

这个代码和上一个代码查找的效果、实现的功能是一模一样的,但是明显要复杂更多。其主要原因是因为这个代码中的SQL语句是一个预编译SQL(参数动态传递 ,使用?作为占位符,然后使用setString方法,为占位符传递参数,最后使用PreparedStatement类进行查询。在查询时,会将这个预编译的SQL和给占位符传递的参数都传给数据库处理。这确实比静态SQL麻烦了不少,但是预编译SQL使用十分广泛,并且安全性、性能都比静态SQL更高

安全性:防止SQL注入

SQL注入 :通过控制输入来修改事先定义好的SQL语句,从而达到执行代码对服务器进行攻击的方法。客户端经常需要提交一些表单给服务端,通过SQL注入,在表单中输入特殊的字符,就可以改变定义好的SQL的意思,从而攻击服务器

常见场景:登录

当用户登录时,需要在表单中输入用户名和密码,然后提交表单给服务端,服务端根据用户提交的用户名和密码在数据库中进行查询(其本质是一个查询数据库的操作),查看是否用户名和密码相匹配。这个SQL语句其实十分简单:

sql 复制代码
select * from user where username = 'root' and password = '123456';

假如能够查询到用户信息,那么就说明用户名和密码都正确,那么就可以登录;反之,用户名和密码至少有一个是错误的,则登录失败。看似这个SQL是没有什么问题的,但是假如在表单中这么输入呢:

用户名:随便输入,比如我输入一串乱码:498asdasas48das689。

密码 :密码的输入就有讲究了,需要输入一些攻击性的内容:' or '1'='1。

这样输入以后,我们将用户名和密码拼接到SQL中看看是什么内容:

sql 复制代码
select * from user where username = '498asdasas48das689' and password = '' or '1'='1';

这么一看SQL语句的意思就完全改变了,无论用户名输入什么,都可以登录,因为'1'='1'永远为true,并且两个条件之间用or连接,这个查询条件就永远为true了,也就代表着用户表中的所有数据都是符合查询条件的,所以说就可以登录了。这就是静态SQL的劣势,十分容易被SQL注入,遭到攻击。然而当使用了预编译的SQL之后,就可以解决这个问题:

预编译SQL是通过参数绑定来解决SQL注入的问题的:

参数绑定

在执行预编译的SQL语句之前 ,需要为预编译SQL中的每个占位符(?)提供具体的参数值。这个过程被称为参数绑定。参数绑定的过程确保了参数值以正确的数据类型传递给数据库 ,并且与SQL语法完全隔离 。这意味着即使参数包含恶意构造的内容(如单引号'或其他特殊字符),它们也不会被解释为SQL代码的一部分,而是作为纯粹的数据处理

高性能

SQL语句的执行流程

如图所示,一条SQL语句从编写到执行是要经过语法检查优化编译三个步骤的,而这三个步骤执行之后 ,这一条SQL语句就会缓存在内存中,假如后面需要使用同样的SQL语句,就可以直接从缓存中"拿"即可,无需再次经过这三步,但是假如有这样三个静态SQL:

sql 复制代码
delete from user where id = 1;
delete from user where id = 2;
delete from user where id = 3;

这三个SQL极其相似,只是查询的条件略有不同,但是就算这样,也必须经过三次编译,才可以执行(分别),无法重复使用,因为这是静态SQL,每次的缓存的SQL内容都是写死了的,只要条件稍微变动,就必须重新编译。

但是假如使用预编译SQL:

sql 复制代码
delete from user where id = ?

然后分别传递参数1、2、3,这样只会在第一次使用SQL进行编译(参数是1),缓存中的内容是delete from user where id = ?,后面传递参数2、3......时,预编译的SQL都可以在缓存中找到,可以直接使用,无需编译 ,极大提高了SQL的性能

相关推荐
·薯条大王1 小时前
MySQL联合查询
数据库·mysql
战族狼魂2 小时前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
xyliiiiiL3 小时前
ZGC初步了解
java·jvm·算法
杉之3 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
morris1313 小时前
【redis】redis实现分布式锁
数据库·redis·缓存·分布式锁
hycccccch4 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq
bobz9654 小时前
k8s 怎么提供虚拟机更好
后端
这个懒人4 小时前
深入解析Translog机制:Elasticsearch的数据守护者
数据库·elasticsearch·nosql·translog
Yan-英杰5 小时前
【百日精通JAVA | SQL篇 | 第二篇】数据库操作
服务器·数据库·sql
bobz9655 小时前
nova compute 如何创建 ovs 端口
后端