为什么我的Java应用每次数据库包编译完成后都需要重启?

Why does my Java application need to be restarted every time after the database package is compiled?

我正在我的应用程序中调用 Oracle 数据库包。如果我重新编译那个包,那么我的应用程序需要重新启动,否则它会说代码已修改。

谁能给我解释一下为什么会这样?

据我所知,您不需要重新启动您的应用程序,只需重新创建与数据库的连接,这是因为驱动程序在内存中保存了一个 link 与您的软件包的先前编译版本因此新连接将获得更新版本。它通常在 PLSQL/Oracle 数据库上观察到,它与驱动程序有关,与 Java 无关。 看看这个 question/answer、Does Tomcat use cached versions of pl/sql modules?,它有一些关于如何克服这种情况的建议。

希望对您有所帮助!

Oracle 包可以有状态信息。在包的主体中,您可以在包级别定义变量。这些 "globals" 存在于对数据库的调用之间,并与数据库会话相关联。重新编译包时(我猜这就是您在 "modified" 错误中看到的内容)您可能已经在包主体中添加或删除了变量,因此 Oracle 必须丢弃包的旧状态做一个新的。它通过引发 ORA-04068: Existing state of packages has been discarded.

来警告您

如果您正在使用某种连接池(包括 Database Resident Connection Pooling),这在 Web 服务器上很常见,您需要记住,当您在代码。当应用服务器完成处理(通过调用 Closed、Dispose 等)时,它刚刚返回到池中,但它保持打开状态并且 Oracle 数据库没有注意到您认为它 "closed"。当需要新连接时,它会从池中获取旧连接并将其提供给服务器。由于 Oracle 从未关闭连接,因此会话自上次使用以来仍处于活动状态。如果自上次使用连接以来包发生了更改,您仍然可以获得 ORA-04068,即使对于您的代码来说,看起来您刚刚打开了一个全新的连接。重新启动您的应用程序服务器会导致池中的所有连接在关闭时关闭并在启动时重新创建,这似乎是您现在解决问题的方式。

  • 如果可以,最好的选择是使用edition based redefinition。这样您就可以编译新包,但只有新会话才会使用新代码。旧会话将继续使用旧代码。同样,如果您正在做错误修复之类的事情,这可能不是理想的选择,因为在旧会话进行修复之前,您将任由旧会话替换新会话。

  • 第二种选择是,如果您知道您不关心该特定包的内部状态是否丢失,那么只是 运行 包 procedure/function 再次调用。 Oracle 不会再给您 ORA-04068(除非再次重新编译包)。

希望这对您有所帮助。如果不是有关您看到的确切错误和您的环境的更多详细信息,将会有所帮助。

重新编译该包后刷新共享池会有所帮助,因为它会强制连接的会话在共享池刷新后首次尝试访问该包时进行重新解析。

具有 DBA 权限: 改变系统刷新shared_pool;

请注意,其他应用程序在刷新共享池后可能会遇到一段时间的缓慢,因为它们的连接会话也将被强制重新解析它们的 SQL/PLSQL 语句,因此建议计划重新编译包并在高峰时间刷新共享池。