当尝试使用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)
答案 0 :(得分: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节的规定,从静态初始化程序调用{{1}}:
JDBC驱动程序必须实现
DriverManager.registerDriver
接口,并且 实现必须包含将被调用的静态初始化器 驱动程序加载时。该初始化器注册一个新实例 本身带有Driver
,如代码示例9-1所示。DriverManager
代码示例9-1用于实现
的驱动程序的示例静态初始化程序public class AcmeJdbcDriver implements java.sql.Driver { static { java.sql.DriverManager.registerDriver(new AcmeJdbcDriver()); } ... }
在加载
java.sql.Driver
实现时,静态初始化程序将 自动注册驱动程序实例。
由于调用了Driver
,它将自动在DriverManager.getDrivers
文件(以及系统属性META-INF/service/java.sql.Driver
中的文件)的类路径中加载驱动程序。
看起来并发现了Oracle JDBC驱动程序,但是随后检查驱动程序是否在jdbc.drivers
的当前类加载器中可用,并且失败了,并显示{{1 }}(检查可以捕获异常,但不能捕获错误,也许应该)。
作为一种解决方法,您应该从类路径中删除Oracle JDBC驱动程序,或者找出为什么它在当前类加载器中不可用。
为进一步诊断,请尝试在代码中调用isDriverAllowed
,NoClassDefFoundError
)甚至是DriverManager.getDrivers()
,然后看看会发生什么。
您可能还想检查Sybase驱动程序的版本,如果有较新的版本不进行此检查,尽管这可能只会导致错误在代码的其他地方发生。
答案 1 :(得分:0)
因此,我做了如下进一步调查。我写了一个最小的完整示例:
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;
}
}
}
然后我使用四个不同的类路径运行此示例,结果如下:
因此,如果同时存在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的计算机上尝试了此操作,并且所有四个类路径版本均成功运行。