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 ? 是不支持的,表名必须拼接),那还是会面临注入风险。这时候就需要你手动做严格的白名单校验了。

相关推荐
学长毕设24 分钟前
基于springboot的食品仓库管理系统
java·spring boot·后端
步行cgn1 小时前
FormHttpMessageConverter 详解:Spring MVC 中表单数据的读写转换器
java·spring·mvc
龙亘川1 小时前
构建城市公共安全数字底座:亘川智城智慧安防系统全维度解读
java·大数据·开发语言·人工智能·信息可视化
金銀銅鐵1 小时前
[Java] 用 CLI 展示 EnumMap 增删键值对时的内部变化
java·源码·ai编程
秋天的怀念_David1 小时前
第二十二夜 · 数字迷雾
java·程序人生·算法
Raas1002 小时前
AI网关部署方案?从部署到上线全流程
java·开发语言·人工智能·gateway·ai网关·企业级ai网关·企业级ai产品
niucloud-admin2 小时前
NIUCLOUD PHP SAAS上门家政使用文档:城市管理
java·前端·php
霸道流氓气质3 小时前
智能客服组件:分层意图识别、RAG Advisor 与人工转接状态机的 Vue3 + Spring AI 生产级实战
java·人工智能·spring
captain3763 小时前
MyBatis操作数据库(入门2)
java·数据库·spring boot·后端·spring·mybatis
心中有你02144 小时前
Java实现Dijkstra最短路径算法(邻接表 + 优先队列)
java·数据库·算法