问题描述
尝试使用JDBC驱动程序访问数据库时,我看到一些奇怪的行为.这是代码片段:
I am seeing some weird behavior when attempting to access a database using JDBC drivers. Here's the code fragment:
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;
}
运行此命令时,这就是我在堆栈跟踪中看到的内容.
When I run this, here's what I see in the stack trace.
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.
So I can see that the driver name that I am trying to get is com.sybase.jdbc4.jdbc.SybDriver which is correct, but for some reason, the DriverManager is looking for oracle.jdbc.OracleDriver.
这是怎么回事?这段代码已经运行了好几年了,我能想到的唯一其他相关信息是我最近将这台机器上的JDK升级到了Open JDK 11.
What is going on? This code has been working well for years, and the only other relevant information I can think of is that I have recently upgraded the JDK on this machine to 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)
推荐答案
这不是一个完整的答案,但似乎与自动驱动程序加载结合在一起是一个类加载问题.
This is not a complete answer, but it seems to be a class loading problem in combination with automatic driver loading.
当您显式使用Class.forName
加载JDBC驱动程序时,该驱动程序应在java.sql.DriverManager
中进行自身注册.
When you explicitly use Class.forName
to load a JDBC driver, the driver should register itself with java.sql.DriverManager
.
查看堆栈跟踪,具体是:
Looking at the stack trace, specifically:
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
:
The Sybase driver incorrectly checks currently registered drivers (using DriverManager.getDrivers
) before (?) registering itself. And even worse, it does this from the driver constructor instead of a static initializer, which could potentially lead to a driver loading deadlock. A proper behaving driver should call DriverManager.registerDriver
from a static initializer as specified in JDBC 4.3 section 9.2:
public class AcmeJdbcDriver implements java.sql.Driver {
static {
java.sql.DriverManager.registerDriver(new AcmeJdbcDriver());
}
...
}
代码示例9-1用于实现java.sql.Driver
CODE EXAMPLE 9-1 Example static initializer for a driver implementing java.sql.Driver
加载Driver
实现时,静态初始值设定项将 自动注册驱动程序的实例.
When a Driver
implementation is loaded, the static initializer will automatically register an instance of the driver.
由于调用了DriverManager.getDrivers
,它将自动在META-INF/service/java.sql.Driver
文件(以及系统属性jdbc.drivers
中的文件)的类路径上加载驱动程序.
Because DriverManager.getDrivers
is called, it will automatically load drivers on the classpath in META-INF/service/java.sql.Driver
files (and those in the system property jdbc.drivers
).
看来是并发现了Oracle JDBC驱动程序,但是随后检查该驱动程序是否在isDriverAllowed
的当前类加载器中可用,并显示NoClassDefFoundError
失败(检查可以捕获异常,但不能捕获错误,也许应该).
It looks like the Oracle JDBC driver was discovered and loaded this way, but then a check if the driver is available in the current class loader in isDriverAllowed
fails with a NoClassDefFoundError
(the check catches exceptions, but not errors, and maybe it should).
作为一种解决方法,您应该从类路径中删除Oracle JDBC驱动程序,或者找出为什么它在当前类加载器中不可用.
As a workaround, you should either remove the Oracle JDBC driver from your class path, or find out why it is not available in the current class loader.
为进一步诊断,请尝试在代码中调用DriverManager.getDrivers()
,Class.forName("oracle.jdbc.Driver
)或什至new oracle.jdbc.Driver()
,然后查看会发生什么情况.
As further diagnosis, try to call DriverManager.getDrivers()
, Class.forName("oracle.jdbc.Driver
) or even new oracle.jdbc.Driver()
in your code and see what happens.
您可能还想检查Sybase驱动程序的版本,如果有较新的版本不进行此检查,尽管这可能只会导致错误在代码的其他地方发生.
You may also want to check the version of your Sybase driver, and if there is a newer version that doesn't do this check, although that may simply cause the error to occur elsewhere in your code.
这篇关于获取java.lang.NoClassDefFoundError:无法初始化类oracle.jdbc.OracleDriver异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!