获取 java.lang.NoClassDefFoundError: 无法初始化类 oracle.jdbc.OracleDriver 异常

use*_*582 5 java sybase jdbc

当我尝试使用 JDBC 驱动程序访问数据库时,我发现一些奇怪的行为。这是代码片段:

LOGGER.debug("driver is " + driver);
try {
    Class.forName(driver);
    LOGGER.debug("got driver");
} catch (Throwable t) {
    LOGGER.debug("throwable getting driver " + driver);
    t.printStackTrace(System.out);
    throw t;
}
Run Code Online (Sandbox Code Playgroud)

当我运行它时,这是我在堆栈跟踪中看到的内容。

08:20:00.417 [main] DEBUG - driver is com.sybase.jdbc4.jdbc.SybDriver
08:20:00.604 [main] DEBUG - throwable getting driver com.sybase.jdbc4.jdbc.SybDriver
java.lang.NoClassDefFoundError: Could not initialize class oracle.jdbc.OracleDriver
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:398)
        at java.sql/java.sql.DriverManager.isDriverAllowed(DriverManager.java:555)
        at java.sql/java.sql.DriverManager.isDriverAllowed(DriverManager.java:547)
        at java.sql/java.sql.DriverManager.getDrivers(DriverManager.java:449)
        at java.sql/java.sql.DriverManager.getDrivers(DriverManager.java:426)
        at com.sybase.jdbc4.jdbc.SybDriver.registerWithDriverManager(Unknown Source)
        at com.sybase.jdbc4.jdbc.SybDriver.<init>(Unknown Source)
        at com.sybase.jdbc4.jdbc.SybDriver.<clinit>(Unknown Source)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:315)
        ... my code
Run Code Online (Sandbox Code Playgroud)

所以我可以看到我试图获取的驱动程序名称是 com.sybase.jdbc4.jdbc.SybDriver,这是正确的,但由于某种原因,DriverManager 正在寻找 oracle.jdbc.OracleDriver。

到底是怎么回事?这段代码多年来一直运行良好,我能想到的唯一其他相关信息是我最近将这台机器上的 JDK 升级到了 Open JDK 11。

>java -version
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
Run Code Online (Sandbox Code Playgroud)

Mar*_*eel 1

这不是一个完整的答案,但它似乎是与自动驱动程序加载相结合的类加载问题。

当您显式使用Class.forName加载 JDBC 驱动程序时,该驱动程序应将自身注册到java.sql.DriverManager.

查看堆栈跟踪,具体来说:

    at java.sql/java.sql.DriverManager.getDrivers(DriverManager.java:426)
    at com.sybase.jdbc4.jdbc.SybDriver.registerWithDriverManager(Unknown Source)
    at com.sybase.jdbc4.jdbc.SybDriver.<init>(Unknown Source)
    at com.sybase.jdbc4.jdbc.SybDriver.<clinit>(Unknown Source)
    at java.base/java.lang.Class.forName0(Native Method)
Run Code Online (Sandbox Code Playgroud)

Sybase 驱动程序DriverManager.getDrivers在 (?) 注册自身之前错误地检查当前注册的驱动程序(使用 )。更糟糕的是,它是从驱动程序构造函数而不是静态初始化程序中执行此操作,这可能会导致驱动程序加载死锁。行为正确的驱动程序应从DriverManager.registerDriver静态初始化程序调用,如 JDBC 4.3 第 9.2 节中指定的那样:

JDBC 驱动程序必须实现该Driver接口,并且实现必须包含一个静态初始化程序,该初始化程序将在加载驱动程序时调用。该初始化程序使用 注册其自身的新实例DriverManager,如代码示例 9-1 所示。

public class AcmeJdbcDriver implements java.sql.Driver {
    static {
        java.sql.DriverManager.registerDriver(new AcmeJdbcDriver());
    }
    ... 
} 
Run Code Online (Sandbox Code Playgroud)

代码示例 9-1 驱动程序实现的静态初始化程序示例java.sql.Driver

Driver加载实现时,静态初始化程序将自动注册驱动程序的实例。

因为DriverManager.getDrivers被调用,它会自动加载文件中类路径上的驱动程序META-INF/service/java.sql.Driver(以及系统属性中的驱动程序jdbc.drivers)。

看起来 Oracle JDBC 驱动程序是通过这种方式发现加载的,但是随后检查该驱动程序在当前类加载器中是否可用会isDriverAllowed失败并显示 a NoClassDefFoundError(该检查捕获异常,但没有捕获错误,也许应该捕获)。

作为解决方法,您应该从类路径中删除 Oracle JDBC 驱动程序,或者找出它在当前类加载器中不可用的原因。

作为进一步的诊断,请尝试调用DriverManager.getDrivers(), Class.forName("oracle.jdbc.Driver) 甚至new oracle.jdbc.Driver()在代码中调用,看看会发生什么。

您可能还需要检查 Sybase 驱动程序的版本,以及是否有较新的版本不执行此检查,尽管这可能只会导致代码中的其他位置发生错误。