【问题标题】:Getting java.lang.NoClassDefFoundError: Could not initialize class oracle.jdbc.OracleDriver exception获取 java.lang.NoClassDefFoundError: 无法初始化类 oracle.jdbc.OracleDriver 异常
【发布时间】:2019-07-24 10:20:06
【问题描述】:

我在尝试使用 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;
}

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

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

所以我可以看到我想要获取的驱动程序名称是 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)

【问题讨论】:

  • 可能您的类路径中没有相应的 jar 文件。你也检查过吗?
  • 你有更深的堆栈跟踪,可能有异常原因吗?
  • 您不需要此代码。自 2007 年以来一直没有必要。只需将其删除即可。
  • 如果我理解正确的话,我删除了我对 Class.forName() 的使用,并检查了所有相关的 JDBC 库 JAR 文件都是 Type 4(即包含 META-INF\services\java.xml)。 sql.Driver 文件)。但是,我仍然看到同样的问题:在应用程序尝试使用 com.sybase.jdbc4.jdbc.SybDriver 时无法初始化类 oracle.jdbc.OracleDriver
  • 我可以通过从类路径中删除 Oracle JDBC 库 JAR 来避免这个错误,但是当应用程序需要同时连接到 Oracle 和 Sybase 数据库时,这并不能解决我在生产中的问题。

标签: java jdbc sybase


【解决方案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)

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

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

public class AcmeJdbcDriver implements java.sql.Driver {
    static {
        java.sql.DriverManager.registerDriver(new AcmeJdbcDriver());
    }
    ... 
} 

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

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

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

看起来 Oracle JDBC 驱动程序是被发现以这种方式加载的,但随后检查驱动程序是否在 isDriverAllowed 中的当前类加载器中可用失败并返回 NoClassDefFoundError( check 捕获异常,但不是错误,也许它应该)。

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

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

您可能还想检查 Sybase 驱动程序的版本,如果有更新的版本不执行此检查,尽管这可能只会导致代码中的其他地方出现错误。

【讨论】:

  • 我还没有解决这个问题,但这很有帮助。我添加了 DriverManager.getDrivers();然后重新运行代码。我应该在这里补充一点,我的类路径包含多个包含 JDBC 驱动程序的 JAR 文件。这些 JAR 文件之一是 ojdbc6-11.1.0.6.0.jar。如果这包含在类路径中,则应用程序会在 DriverManager.getDrivers() 行引发错误。如果我从类路径中删除这个 JAR 文件,那么我可以按预期看到其他 JDBC 驱动程序的列表。用 ojdbc7.jar 替换没有帮助。我将尝试看看升级 Sybase 驱动程序是否有帮助...
  • @user304582 当您使用 Java 11(或至少 9 或更高版本)时,您可能想尝试更新版本的 Oracle JDBC 驱动程序(例如 18.3)。我不知道错误的确切上下文,因此很难提供更具体的帮助。发布完整的异常堆栈跟踪可能会有所帮助,并更多地描述您的应用程序(例如,它是一个简单的应用程序,还是一个 Java EE 应用程序,或者......,依赖关系是如何安排的,等等)。如果您可以通过创建 minimal reproducible example 将其减少到最低限度,那也会有所帮助。
【解决方案2】:

所以我做了一些进一步的调查如下。我写了一个最小的完整示例:

import java.sql.*;
import java.util.*;

public class TestDrivers {

    public static void main(String[] args) {
        try {
            Enumeration<Driver> driverEnumeration = DriverManager.getDrivers();
            while (driverEnumeration.hasMoreElements()) {
                Driver driver = driverEnumeration.nextElement();
                System.out.println("driver is " + driver.getClass().getName());
            }
        } catch (Throwable t) {
            System.out.println("throwable getting drivers");
            t.printStackTrace(System.out);
            throw t;
        }
    }
}

然后,我使用四个不同的类路径运行此示例,结果如下:

  1. 没有驱动程序 JAR 文件:成功,没有列出驱动程序
  2. 驱动程序 JAR 文件:jconn4.jar;jtds-1.3.1.jar;ojdbc6-11.1.0.6.0.jar:java.lang.NoClassDefFoundError 失败:无法初始化类 oracle.jdbc.OracleDriver
  3. 驱动程序 JAR 文件:jtds-1.3.1.jar;ojdbc6-11.1.0.6.0.jar:成功列出了 jTDS 和 Oracle 驱动程序
  4. 驱动程序 JAR 文件:jconn4.jar;jtds-1.3.1.jar:成功列出了 jTDS 和 Sybase 驱动程序

因此,如果同时存在 Oracle 和 Sybase JDBC 驱动程序,则会发生一些奇怪的交互。我还尝试了 Oracle ojdbc7.jar 和 ojdbc8.jar 文件,结果基本相同。这是完整的堆栈跟踪:

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.javamarket.drivers.TestDrivers.main(TestDrivers.java:10)
Exception in thread "main" 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.javamarket.drivers.TestDrivers.main(TestDrivers.java:10)

最后,我在另一台运行 JDK 8 的机器上进行了尝试,所有四个类路径变体都成功运行。

【讨论】:

  • 您是否也尝试过使用较新版本的 Oracle JDBC 驱动程序(例如 18.3)?另外,这是哪个版本的 Sybase 驱动程序?
  • 另外,如果您使用java -Xlog:class*=debug 运行它,日志记录是什么
猜你喜欢
  • 2019-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-10
  • 1970-01-01
  • 2013-08-08
相关资源
最近更新 更多