程序员潇然 发表于 2022-8-27 16:40:46

JDBC驱动程序注册 JDBC简介(二)

使用JDBC进行数据库操作的第一步就是驱动注册(当然你得先导入JAR)。

驱动注册有多种方式,第一步必然是获得正确的驱动名称与URL格式


### 驱动名称与URL格式


| RDBMS      | 驱动程序名称                  | URL格式                                          |
| ------------ | --------------------------------- | ---------------------------------------------------- |
| MySQL      | com.mysql.jdbc.Driver         | jdbc:mysql://hostname/databaseName               |
| ORACLE   | oracle.jdbc.driver.OracleDriver | jdbc:oracle:thin:@hostname:portNumber:databaseName |
| PostgreSQL | org.postgresql.Driver         | jdbc:postgresql://hostname:port/dbname             |
| DB2      | com.ibm.db2.jdbc.net.DB2Driver| jdbc:db2:hostname:port Number/databaseName         |
| Sybase   | com.sybase.jdbc.SybDriver       | jdbc:sybase:Tds:hostname: portNumber/databaseName|


常用的驱动程序名称与URL格式如上表所示,**随着版本变化,会存在些许变动,如有不对,还需重新查验**


比如MYSQL中5与6 ,**mysql5用的驱动是com.mysql.jdbc.Driver,mysql6以后用的是com.mysql.cj.jdbc.Driver**

#### MYSQL的驱动下载

MYSQL的connector下载地址

`https://dev.mysql.com/downloads/connector/`

!(data/attachment/forum/202208/27/163427fgkwl2lblxg6lirx.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")


另外还有完整的文档

`https://dev.mysql.com/doc/index-connectors.html`

#### oracle驱动下载

`https://www.oracle.com/technetwork/database/application-development/jdbc/downloads/index.html`

!(data/attachment/forum/202208/27/163513jzmyaozz0qwkoygy.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")


### 驱动注册

在通过驱动管理器创建连接前需要进行驱动注册

驱动注册有三种形式

Class.forName("com.mysql.jdbc.Driver");

DriverManager.registerDriver(new com.mysql.jdbc.Driver());

System.setProperty("jdbc.drivers","com.mysql.jdbc.Driver");
可以查看mysql-connector中的源码

```java
package com.mysql.jdbc;

import com.mysql.cj.jdbc.NonRegisteringDriver;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {

//

// Register ourselves with the DriverManager

//
    static {
      try {
            java.sql.DriverManager.registerDriver(new Driver());
      } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
      }
    }


    /**
   * Construct a new driver and register it with DriverManager
   *
   * @throws SQLException
   * if a database error occurs.
   */

    public Driver() throws SQLException {
// Required for Class.forName().newInstance()

    }

}
```

**类加载时,会执行<clinit>方法,在jvm第一次加载class文件时调用,包括静态变量初始化语句和静态块的执行 。**

所以Class.forName("com.mysql.jdbc.Driver");会调用java.sql.DriverManager.registerDriver(new Driver());完成了驱动的注册

所以直接调用java.sql.DriverManager.registerDriver(new Driver()); 也是可以的

驱动管理器类加载时,也会从系统属性"jdbc.drivers" 中获取数据

所以,在驱动管理器加载之前,可以使用    System.setProperty("jdbc.drivers","com.mysql.jdbc.Driver"); 进行设置

也可以同时设置多个比如System.setProperty("jdbc.drivers","XXXDriver:XXXDriver:XXXDriver");

!(data/attachment/forum/202208/27/163620f02thuur0jj4ghl2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")


* DriverManager.registerDriver(new com.mysql.jdbc.Driver());依赖具体,没有面向抽象编程,不推荐
* System.setProperty("jdbc.drivers","com.mysql.jdbc.Driver"); DriverManager加载前有用,不够灵活方便
* Class.forName("com.mysql.jdbc.Driver"); 通常使用

#### 自动加载的4.0时代

从JDBC4.0开始,就不在需要主动的显式加载,对应JDK6

在调用 getConnection 方法时,DriverManager 会试着从初始化时加载的那些驱动程序以及使用与当前 applet 或应用程序相同的类加载器显式加载的那些驱动程序中查找合适的驱动程序。

!(data/attachment/forum/202208/27/163645uibnnwnmrnzng8z8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

本文作者:程序员潇然 疯狂的字节X https://crazybytex.com/

**JDBC 4.0 的规范规定,所有 JDBC 4.0 的驱动 jar 文件必须包含一个 java.sql.Driver,它位于 jar 文件的 META-INF/services 目录下。这个文件里每一行便描述了一个对应的驱动类**

**在启动项目或是服务时,会判断当前classspath中的所的jar包,并检查他们META-INF目录下,是否包含services文件夹,如果包含,就会将里面的配置加载成相应的服务。**

`META-INF/services/jdbc.sql.Driver`文件内容为需要加载的驱动,如下图所示

!(data/attachment/forum/202208/27/163657wkxg4mzyiiv05kco.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

可以打印出所有的已经加载的驱动程序,如下图所示,与上图文件中的不谋而合

!(data/attachment/forum/202208/27/163712mkwi23lq23cggrck.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")


所以,对于4.0前,使用Class.forName

4.0后我们只需要将JAR包管理好就足够了,这一切都会有人帮我们完成(当然前提是需要使用配套的驱动Jar包)

完整的loadInitialDrivers方法

```java
private static void loadInitialDrivers() {
      String drivers;
      try {
                        //获取环境变量中jdbc.drivers的列表
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                  return System.getProperty("jdbc.drivers");
                }
            });
      } catch (Exception ex) {
            drivers = null;
      }
      //如果按照规范在jar包中的META-INF/services设置了文件,将会加载为服务
      // If the driver is packaged as a Service Provider, load it.
      // Get all the drivers through the classloader
      // exposed as a java.sql.Driver.class service.
      // ServiceLoader.load() replaces the sun.misc.Providers()
      AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();
                /* Load these drivers, so that they can be instantiated.
               * It may be the case that the driver class may not be there
               * i.e. there may be a packaged driver with the service class
               * as implementation of java.sql.Driver but the actual class
               * may be missing. In that case a java.util.ServiceConfigurationError
               * will be thrown at runtime by the VM trying to locate
               * and load the service.
               *
               * Adding a try catch block to catch those runtime errors
               * if driver not available in classpath but it's
               * packaged as service and that service is there in classpath.
               */
                try {
                                        //依次加载所有驱动,访问过后不就加载了嘛
                  while (driversIterator.hasNext()) {
                        driversIterator.next();
                  }
                } catch (Throwable t) {
                                // Do nothing
                }
                return null;
            }
      });
      println("DriverManager.initialize: jdbc.drivers = " + drivers);
                //如果环境变量中没有设置的驱动程序,就可以结束了
                //否则就将环境变量中的驱动程序加载一下
      if (drivers == null || drivers.equals("")) {
            return;
      }
      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);
            }
      }
    }

```


简言之,loadInitialDrivers主要做了两件事情:

* 读取系统属性jdbc.drivers,如果为空就拉到,如果存在将会进行加载。
* 检查jar包中的META-INF/services,如果有那么会自动注册,服务注册依赖的是ServiceLoader

对于加载的驱动程序,在前面已经说过,静态代码块中,会完成注册,而注册就是添加到CopyOnWriteArrayList<DriverInfo> registeredDrivers中

`本文作者:程序员潇然 疯狂的字节X https://crazybytex.com/`

!(data/attachment/forum/202208/27/163959fqsddkwszqq2s9z5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")


### 总结

驱动注册就是加载数据库的驱动程序,是使用JDBC进行数据库操作的第一步。

JDBC的桥接模式,提供给应用程序开发者的API是抽象,这个驱动程序就是实现,如果不能进行成功注册,就没办法提供后续的服务了,所以驱动注册很重要。

以 JDBC4.0为分水岭

如果按照JDBC4.0后的规范,在jar包中设置了META-INF/services/jdbc.sql.Driver文件,并且写入了驱动,那么会自动加载

如果是之前的,则需要使用Class.forName()进行显式加载。

自动加载依赖ServiceLoader.load(Driver.class);,其中关于目录的设置是写死的PREFIX = "META-INF/services/",想要深入研究可以翻阅ServiceLoader

!(data/attachment/forum/202208/27/164011dmeztyhct0rgrz6n.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

!(data/attachment/forum/202206/16/141330jha7st9soow8772i.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "common_log.png")
`转载务必注明出处:程序员潇然,疯狂的字节X,https://crazybytex.com/thread-169-1-1.html `
页: [1]
查看完整版本: JDBC驱动程序注册 JDBC简介(二)