Oracle JDBC驱动默认implicitCachingEnabled为true但statementCacheSize默认为0,故必须同时设置setImplicitCachingEnabled(true)和setStatementCacheSize(N)才生效;仅设后者无效。
oracle jdbc 驱动默认开启 implicit caching,但不配 statementcachesize 就等于白开——缓存大小为 0,所有 preparedstatement 都无法复用。
为什么 setStatementCacheSize(100) 没生效?
常见错误是只调用了 connection.setStatementCacheSize(100),却没启用隐式缓存开关。从 Oracle 12.2.0.1 起,implicitCachingEnabled 默认为 true,但这个“启用”只是允许缓存逻辑运行,真正决定是否缓存、缓多少的,是 statementCacheSize 值本身。
如果该值为 0(默认值),即使 implicitCachingEnabled 是 true,驱动内部的 StatementCache 也不会存任何对象——SoftHashMap 里始终为空。
- 必须成对设置:
connection.setImplicitCachingEnabled(true)+connection.setStatementCacheSize(N) - 注意:此配置只对当前
Connection实例有效;连接池场景下,需在连接创建后、首次使用前完成设置(例如 HikariCP 的connection-init-sql不适用,应改用datasource-properties或自定义ConnectionCustomizer) - 若用的是
OraclePooledConnection,它继承自PooledConnection,其缓存行为由底层物理连接控制,仍需在获取到OracleConnection后显式调用上述两个方法
statementCacheSize 设多少才合理?
不是越大越好。缓存过大会导致 PreparedStatement 对象长期驻留堆内存,尤其当 SQL 模板中包含大字段(如 CLOB/BLOB)或游标未关闭时,容易触发 ORA-01000: maximum open cursors exceeded 或 JVM OOM。
- 典型经验值:20–50,高并发 OLTP 系统建议从 30 起步,配合监控调整
- 每增加 1 个缓存项,至少多占几百字节(含 SQL 字符串、参数元信息、驱动内部状态);100 个缓存 ≈ 100–200KB 堆内存常驻
- 缓存键是 SQL 字符串全匹配(不含空格/换行差异),所以
"SELECT * FROM t WHERE id = ?"和"select * from t where id = ?"被视为两条不同语句,各自占用一个槽位 - MyBatis/Spring JDBC 等框架若启用了自己的预编译缓存(如
spring.jdbc.template.cache-prep-stmts),要与 JDBC 层缓存错开层级,避免重复缓存同一 SQL
如何验证缓存是否真在工作?
不能只看日志或文档描述。最直接的方式是观察 PreparedStatement 对象的哈希码复用情况:
立即学习“Java免费学习笔记(深入)”;
PreparedStatement ps1 = conn.prepareStatement("SELECT * FROM emp WHERE deptno = ?");
System.out.println("ps1 hash: " + System.identityHashCode(ps1));
PreparedStatement ps2 = conn.prepareStatement("SELECT * FROM emp WHERE deptno = ?");
System.out.println("ps2 hash: " + System.identityHashCode(ps2));若缓存生效,两次返回的 ps1 和 ps2 很可能指向同一个对象(identityHashCode 相同);若不同,说明缓存未命中或未启用。
- 更可靠的方法是启用 Oracle JDBC trace:
-Doracle.jdbc.Trace=true -Djava.util.logging.config.file=logging.properties,搜索日志中cacheHit或cacheMiss关键字 - 注意:
clearCache()必须在连接被归还池前手动调用(尤其在连接复用频繁的场景),否则上一个请求缓存的语句会污染下一个请求的执行环境 - 不要依赖连接池的“自动清理”——HikariCP、Druid 等均不干预
PreparedStatement缓存生命周期
隐式缓存和显式缓存(Explicit Caching)的区别
Oracle JDBC 提供两种缓存模式:IMPLICIT(默认)和 EXPLICIT。前者由驱动自动管理生命周期,后者需你调用 closeWithKey(String) 显式指定缓存键,并用 getStatement(String) 取回。
-
IMPLICIT更适合通用业务代码,但要求 SQL 模板严格一致、且不能混用Statement/PreparedStatement -
EXPLICIT适用于需要跨连接复用语句的特殊场景(如批处理模板),但极易因 key 冲突或忘记getStatement导致空指针;OracleStatement.EXPLICIT常被误用为开关,实际它只是枚举值,需配合setStatementCacheMode() - 二者不可同时启用;启用
EXPLICIT后,setStatementCacheSize()对IMPLICIT无效,需改用setExplicitCachingEnabled(true)和独立 size 控制
缓存不是银弹。SQL 模板越稳定、参数变化越少,缓存收益越高;一旦出现动态拼接列名、表名或条件分支,缓存命中率会断崖下跌。真正要盯住的,是缓存背后那个被反复打开又忘记关闭的 ResultSet ——它比缓存大小更能决定你的应用会不会突然 OOM。


















