PreparedStatement 复用可避免重复编译,需在循环外创建同一实例并确保SQL字符串完全一致,配合连接池启用预编译缓存,且仍须显式close。

PreparedStatement 本身的设计就天然避免了 SQL 的重复编译,关键在于**复用同一个 PreparedStatement 实例**,而不是每次执行都重新创建。
复用 PreparedStatement 实例
PreparedStatement 在首次创建时(即调用 Connection.prepareStatement(sql)),会将 SQL 发送给数据库进行解析、校验和执行计划生成(即“编译”)。后续只要使用同一实例调用 setXxx() 和 execute(),数据库通常会直接复用已缓存的执行计划,跳过重复编译。
- ✅ 正确做法:在循环或多次查询前创建一次 PreparedStatement,然后在循环内只设参、执行
- ❌ 错误做法:在循环内部反复调用
connection.prepareStatement(...)
注意 SQL 字符串必须完全一致
只有当传入的 SQL 字符串字面量(包括空格、换行、大小写)完全相同时,数据库才可能命中预编译缓存。例如:
-
"SELECT * FROM user WHERE id = ?"和"select * from user where id = ?"在某些数据库(如 MySQL 默认配置)中可能视为不同语句 - 拼接出来的动态 SQL(如
"SELECT * FROM " + table + " WHERE id = ?")必然导致每次 SQL 不同,无法复用
配合连接池合理管理生命周期
PreparedStatement 的复用不局限于单次请求,更适用于连接池场景:
立即学习“Java免费学习笔记(深入)”;
- 连接池中的物理连接被复用时,其上创建的 PreparedStatement 也可能被复用(取决于驱动和数据库支持,如 Oracle 的 implicit statement cache、MySQL Connector/J 的
cachePrepStmts=true) - 建议在连接池配置中启用预编译缓存(如 HikariCP 可配
dataSource.cachePrepStmts=true,MySQL 驱动加参数&cachePrepStmts=true&prepStmtCacheSize=250) - 不用手动 close PreparedStatement?不,仍需显式 close(或 try-with-resources),否则可能泄漏资源;close 是释放客户端资源,不影响服务端缓存
避免隐式创建 Statement 的陷阱
以下操作会导致绕过 PreparedStatement 缓存机制:
- 调用
prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ...)等带额外参数的重载方法,部分驱动会禁用缓存 - 使用
createStatement()+executeUpdate("UPDATE ... SET x=" + value)—— 这是字符串拼接,既不安全也不可缓存 - 在 PreparedStatement 中混用非参数化部分(如表名、列名)——这些无法参数化,只能靠拼接,破坏 SQL 稳定性


















