JDBC4中驱动程序类是如何定位的?

12

JDBC第4版的一个伟大的新增功能是,您不必再通过调用Class.forName来显式加载驱动程序。当应用程序首次尝试连接数据库时,DriverManager会自动加载在应用程序CLASSPATH中找到的驱动程序。

我的问题是如何实现的?如果类路径中有多个驱动程序怎么办?

我能猜到的一件事是,在解析连接URL时是否需要JDBC或ODBC驱动程序可以被找到,但是如何从多个符合JDBC标准的驱动程序中选择适用于我所使用的数据库(比如说我正在使用MySql,我需要MySql-Connector驱动程序)呢?在JVM中有任何这些数据库驱动程序的静态映射吗?


为什么需要驱动程序? - Roman C
2
你还有什么其他方法连接数据库呢?传统的方式是使用Class.ForName(driver)。 - Aniket Thakur
@atamanroman 我没有什么需要向你解释的,把你的行话从SO中拿出去。 - Roman C
3个回答

23
每个符合JDBC 4标准的驱动程序在其jar文件中都有一个名为 META-INF/services/java.sql.Driver 的文件,在该文件中列出了其实现的 java.sql.Driver。当您请求连接时,DriverManager 将使用ServiceLoader查找类路径中所有 META-INF/services/java.sql.Driver 的副本,然后加载列出的所有类。当加载一个 java.sql.Driver 类时,它必须向 DriverManager 注册自己,以便每个 Driver 实现都可以注册自己。
DriverManager 请求连接时,DriverManager 将遍历所有已注册的驱动程序,并要求它们提供一个 Connection。驱动程序将使用JDBC URL检查其是否支持协议(例如,Jaybird/Firebird JDBC 检查URL是否以 "jdbc:firebirdsql:"或者 "jdbc:firebird:"开头)如果驱动程序不支持该协议,则返回 null;如果它支持该协议,则它将返回已建立的连接,否则将抛出 SQLException(例如,如果URL中存在错误或无法连接)。如果所有驱动程序都返回 null(没有支持该协议的驱动程序),那么 DriverManager 将抛出一个带有错误消息 "No suitable driver found for <url>"SQLException。所以,只要不同的驱动程序支持不同的协议,多个驱动程序在类路径上并不重要,然而,如果存在相同数据库的多个驱动程序(或至少是相同的协议前缀),它将使用驱动程序列表中的第一个。根据Java版本,如果该驱动程序失败并出现 SQLException ,它将继续尝试下一个驱动程序(至少Java 5及更高版本),或停止尝试并抛出异常(我认为这是在Java 1.4或甚至更早的版本中)。

我们可以列出已经在机器上注册的所有驱动程序吗? - Joseph_Marzbani
1
@Joseph_Marzbani 是的,查看DriverManager的API,具体来说是drivers()getDrivers()方法。 - Mark Rotteveel

12

以下信息摘自于 http://www.onjava.com/2006/08/02/jjdbc-4-enhancements-in-java-se-6.html,介绍了JDBC4驱动程序的加载方式。

当调用getConnection方法时,DriverManager会尝试从在初始化时加载的JDBC驱动程序以及使用当前应用程序相同的类加载器显式加载的驱动程序中寻找合适的驱动程序。

DriverManager的getConnection和getDrivers方法已经增强以支持Java SE服务提供程序机制(SPM)。根据SPM的定义,服务被定义为一组众所周知的接口和抽象类,而服务提供者则是服务的具体实现。它还指定服务提供程序配置文件存储在META-INF / services目录中。 JDBC 4.0驱动程序必须包括文件META-INF / services / java.sql.Driver。该文件包含JDBC驱动程序实现java.sql.Driver的名称。例如,要加载用于连接到Apache Derby数据库的JDBC驱动程序,则META-INF /services/java.sql.Driver文件将包含以下条目:

org.apache.derby.jdbc.EmbeddedDriver

现在来回答你的问题。

我的问题是如何解决?如果类路径中有多个驱动程序怎么办?

作为类加载器的规则,首先找到的任何类将被加载,如果它已经被加载,则不会被类加载器重新加载。


0

你的问题的答案可以从java.sql.DriverManager类中的loadInitialDrivers方法中获得。

drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
    public String run() {
                return System.getProperty("jdbc.drivers");
            }
        });  

如果程序发现多个JDBC驱动程序,则会使用以下逻辑来选择正确的驱动程序。
    String[] driversList = drivers.split(":");
    println("number of Drivers:" + driversList.length);
    for (String aDriver : driversList) {
        try {
            println("DriverManager.Initialize: loading " + aDriver);
            Class.forName(aDriver, true,
                    ClassLoader.getSystemClassLoader());
        } catch (Exception ex) {
            println("DriverManager.Initialize: load failed: " + ex);
        } 

这不是JDBC 4.0中引入的自动驱动程序加载,而是一种较旧(但仍受支持)的机制,允许您在系统属性jdbc.drivers中指定驱动程序类名。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接