JDBC PreparedStatement的作用机制

为什么说jdbc的PreparedStatement可以完全避免SQL注入的问题,还能高效利用数据库本身对查询的缓存?它的机制是什么,是数据库层面做的吗?


1 为什么能完全防范 SQL 注入?

JDBC Statement 中,SQL 语句是通过字符串拼接完成的。数据库拿到的是一串分不清哪里是"命令"、哪里是"数据"的字符。

  • 普通 Statement 的漏洞

    如果你拼接 SELECT * FROM users WHERE username = ' + admin' OR '1'='1 + ',数据库看到的指令是: "选出所有用户,只要 1 等于 1 就行" 。数据篡改了逻辑。

  • PreparedStatement :

    它采用了参数化查询(Parameterized Query)

    1. 预编译阶段 :驱动程序先向数据库发送一个带占位符的模版:SELECT * FROM users WHERE username = ?
    2. 数据绑定阶段 :再把具体的参数(如 admin' OR '1'='1)传过去。

关键点 :由于数据库已经提前完成了 SQL 的词法和语法分析,它已经认定这个 ? 位置只能是纯文本数据 。哪怕你传入的数据里包含 DROP TABLE 或者 OR 1=1,数据库也只会把它当成一个"名字特别奇怪的用户",而不会将其当作指令执行。


2 为什么能高效利用缓存?

这就涉及到数据库执行 SQL 的过程:解析(Parse) -> 优化(Optimize) -> 执行(Execute)

  • 硬解析(Hard Parse) :如果每次 SQL 字符串都不同(比如 ID 一直变),数据库就得针对每一条 SQL 重新计算执行计划(怎么查最快、用哪个索引),这非常耗 CPU。
  • 软解析(Soft Parse)PreparedStatement 允许数据库缓存执行计划

因为你发送的 SQL 模版始终是 SELECT * FROM items WHERE id = ?,数据库会发现:"诶,这个模版我见过,执行计划已经算好了,直接拿来用吧!"。

注意 :这种缓存主要发生在数据库的 SGA(系统全局区)/ Library Cache 中,而不是在 Java 程序里。


3 它的机制是什么?是数据库层面做的吗?

这是一个端到端(客户端 + 服务端) 的配合过程,其核心协议通常被称为 二进制协议(Binary Protocol)

3.1 宏观流程:

  1. 客户端(JDBC Driver) :将带有 ? 的 SQL 发给数据库,并请求一个 Statement ID

  2. 服务端(Database)

    • 校验 SQL 语法。
    • 生成执行计划。
    • 将该计划与 Statement ID 绑定并存入缓存。
    • 返回 ID 给客户端。
  3. 客户端 :调用 setInt()setString(),将纯数据(不含 SQL 关键字)和 Statement ID 打包发送。

  4. 服务端:根据 ID 找到缓存好的计划,直接把数据塞进去运行。

3.2 是不是所有数据库都支持?

  • 数据库支持:大多数现代关系型数据库(MySQL, PostgreSQL, Oracle, SQL Server)都在内核层面支持预编译。

  • 驱动模拟:如果某些老旧数据库不支持,JDBC 驱动会在本地进行"模拟(Client-side emulation)",即在本地安全地转义字符串再发过去。

    • 冷知识 :在 MySQL 中,默认情况下 JDBC 是在本地模拟预编译的。如果你想让它在数据库层面真正"硬核"执行,需要在连接字符串里加上 useServerPrepStmts=true

4 总结

PreparedStatement 的本质是把 SQL 变成了"带参数的函数"

  • 安全性:数据永远不会被误认为指令(类型安全)。
  • 性能:一次编译,多次运行(避免重复计算执行计划)。

稍微多说一句: 虽然它能防住 99% 的注入,但如果你在 SQL 模版里依然用字符串拼接表名(比如 SELECT * FROM ? 是不支持的,表名必须拼接),那还是会面临注入风险。这时候就需要你手动做严格的白名单校验了。

相关推荐
Patrick在香港1 小时前
Python Docker镜像从1.2GB到89MB:多阶段构建的完整优化实录
java·python·docker·信息可视化·容器·数据分析·ai编程
上海安当技术1 小时前
敏感数据怎么防拖库?信封加密(DEK+KEK 二层密钥)架构设计与 Java 实战
java·开发语言·python
我是唐青枫1 小时前
Java JCommander 实战详解:用注解解析命令行参数
java·开发语言
大鹏说大话1 小时前
C++ 内存布局详解:类、虚函数、虚表底层原理
java·开发语言
还是鼠鼠2 小时前
Spring AI连接DeepSeek与通义千问:application.yaml配置详解
java·通义千问·spring ai·deepseek
计算机小白一个3 小时前
蓝桥杯 Java B 组之哈希表应用(两数之和、重复元素判断)
java·数据结构·算法·蓝桥杯
sunburn-4 小时前
Java 队列全面详解:从入门到面试实战
java·开发语言·汇编·ide·idea
vx-程序开发4 小时前
springboot农产品运输服务平台---附源码75498
java·javascript·spring boot·python·eclipse·django·php
zzh___zzh6 小时前
SQL 窗口函数 ROW_NUMBER、RANK、DENSE_RANK 常用方法笔记
java
计科土狗6 小时前
GESP六级专题之类与对象
java·前端·数据库