问题
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
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.
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)
回答1:
This is not a complete answer, but it seems to be a class loading problem in combination with automatic driver loading.
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)
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:
JDBC drivers must implement the
Driver
interface, and the implementation must contain a static initializer that will be called when the driver is loaded. This initializer registers a new instance of itself with theDriverManager
, as shown in CODE EXAMPLE 9-1.public class AcmeJdbcDriver implements java.sql.Driver { static { java.sql.DriverManager.registerDriver(new AcmeJdbcDriver()); } ... }
CODE EXAMPLE 9-1 Example static initializer for a driver implementing
java.sql.Driver
When a
Driver
implementation is loaded, the static initializer will automatically register an instance of the driver.
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
).
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).
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.
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.
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.
回答2:
So I did some further investigation as follows. I wrote a minimal complete example:
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;
}
}
}
I then ran this example with four different classpaths with the following results:
- No driver JAR files: success, no drivers listed
- Driver JAR files: jconn4.jar;jtds-1.3.1.jar;ojdbc6-11.1.0.6.0.jar: failure with java.lang.NoClassDefFoundError: Could not initialize class oracle.jdbc.OracleDriver
- Driver JAR files: jtds-1.3.1.jar;ojdbc6-11.1.0.6.0.jar: success with jTDS and Oracle drivers listed
- Driver JAR files: jconn4.jar;jtds-1.3.1.jar: success with jTDS and Sybase drivers listed
So there is some weird interaction going on if both Oracle and Sybase JDBC drivers are present. I also tried the Oracle ojdbc7.jar and ojdbc8.jar files with essentially the same results. Here's the full stacktrace:
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)
Finally, I tried this on a different machine running JDK 8 and all four classpath variations run successfully.
来源:https://stackoverflow.com/questions/54960627/getting-java-lang-noclassdeffounderror-could-not-initialize-class-oracle-jdbc-o