ORA-04068是Oracle会话级缓存一致性机制触发的正常错误,因其他会话重编译含全局变量/常量的包导致当前连接状态失效,首次调用即报错并伴随ORA-04061、ORA-06508。
ORA-04068 是什么,为什么第一次调用就报错?
ora-04068 不是数据库故障,而是 oracle 会话级缓存一致性机制触发的正常行为。核心原因是:某个 package(尤其是含全局变量或常量的“有状态包”)被其他会话重新编译后,当前 jdbc 连接持有的该包状态副本(包括变量值、初始化逻辑等)被 oracle 主动丢弃。此时再执行该包内过程,就会抛出 ora-04068,并伴随 ora-04061(包体失效)、ora-06508(找不到程序单元)。
典型场景包括:
- 开发时用 PL/SQL Developer 或 SQL*Plus 修改并编译了
PKG_COMMON,而 Java 应用仍复用旧连接池中的连接 - 定时 Job 中先
DROP TABLE再CREATE TABLE,导致依赖该表的存储过程(如P_INSERT_TB)元数据失效 - RAC 环境下节点间时间不同步或对象时间戳不一致(
STIME != P_TIMESTAMP),引发隐式状态丢弃
Java 应用中捕获并自动重试是最实用的方案
重启应用或杀 session 属于“治标”,线上不可行;避免改包又不现实。最可控的做法是在 JDBC 层拦截异常并透明重试一次——因为第二次调用时 Oracle 已完成状态重建,必然成功。
示例(Spring + JdbcTemplate 场景):
try {
jdbcTemplate.execute("BEGIN mypkg.myproc(:1); END;", ps -> {
ps.setString(1, "val");
});
} catch (DataAccessException e) {
if (e.getRootCause() instanceof SQLException sqlEx &&
"4068".equals(sqlEx.getSQLState())) { // 注意:不是 getErrorCode()
// 重试一次
jdbcTemplate.execute("BEGIN mypkg.myproc(:1); END;", ps -> {
ps.setString(1, "val");
});
} else {
throw e;
}
}关键点:
- 必须检查
SQLState为"4068",Oracle 的getErrorCode()返回的是4068,但 JDBC 规范要求用SQLState判断标准错误类 - 不要无脑重试所有 SQL 异常,仅对明确已知可恢复的
ORA-04068做处理 - 若使用 MyBatis,可在
Interceptor中统一拦截SQLException并判断sqlState
包设计阶段就要规避状态依赖
真正一劳永逸的方式,是从源头减少“有状态包”的使用。只要包里定义了任何全局变量(V_MY_VAR VARCHAR2(10))或常量(C_FLAG CONSTANT NUMBER := 1),它就是有状态的,每次编译都会触发 ORA-04068。
可行策略:
- 把常量抽到独立的“只读包”中(如
PKG_CONSTANTS),这个包极少修改,且不包含逻辑,编译影响小 - 业务逻辑包(如
PKG_ORDER)只做纯函数式操作,所有上下文通过参数传入,不依赖包级变量 - 必须维护状态时,改用数据库表或临时表(
GLOBAL TEMPORARY TABLE)替代包变量,状态持久化与会话解耦 - 避免在包体中直接引用动态 DDL 影响的对象(如
EXECUTE IMMEDIATE 'DROP TABLE t'),改用DBMS_SQL或前置校验
紧急排查时别忽略视图和依赖时间戳
有些 ORA-04068 并非人为编译引起,而是底层对象(尤其是视图)时间戳异常。在 RAC 或长期运行的库中,可能出现 obj$.stime 和 dependency.p_timestamp 不一致,Oracle 会主动丢弃包状态。
快速验证命令:
SELECT do.name d_name, po.name p_name,
TO_CHAR(po.stime, 'YYYY-MM-DD HH24:MI') stime,
TO_CHAR(p_timestamp, 'YYYY-MM-DD HH24:MI') p_ts,
DECODE(SIGN(po.stime - p_timestamp), 0, 'OK', '*MISMATCH*') chk
FROM sys.obj$ do, sys.dependency d, sys.obj$ po
WHERE d.p_obj# = po.obj#(+)
AND d.d_obj# = do.obj#
AND do.status = 1 AND po.status = 1
AND po.stime != p_timestamp;若输出大量 *MISMATCH*,说明存在隐式失效风险。此时单纯重编译包无效,需联系 DBA 检查是否需打补丁或执行 DBMS_MVIEW.REFRESH 类操作。
真正棘手的从来不是错误本身,而是那个“改完包忘了通知下游应用刷新连接池”的人,以及那些藏在 PKG_COMMON 里、没人敢动的全局常量。


















