JDBC之Driver和DriverMananger

JDBC之Driver和DriverMananger

Java和MySQL的关系

java是在内存中运行的,但是Java想要统一天下,那么就不得不涉及到对于数据的处理。

那么内存中数据资源肯定是有限的,那么处理磁盘或者是数据库中的程序就成了问题。

Java早期属于Sun公司,而MySQL作为一个非常优秀的关系型数据库,也被Sun公司看中。

所以Sun公司收购了MySQL,但是因为MySQL分为了CLIENT和SERVER端

CLIENT连接SERVER端,可以发送具体的指令来连接上SERVER,如下所示:

mysql -hlocalhost -uroot -proot -Proot

但是手动使用MySQL自带的客户端,相对于程序来说,是不合适的。所以指定了一套Java能够操作MySQL的规范。

但是随着数据库的产生如雨后春笋般涌出,如Oracle等,那么Java为了抢占市场,也能够操作其他的数据库,而不是只支持自己家的MySQL。

Java制定了一套连接数据库的规范,这就是大名鼎鼎的JDBC。

JDBC

概念:Java Database connect java版本的数据库连接

只有在连接上数据库的条件下,才能够来操作数据库。所以在JDBC中提供出来对应的接口。

JDBC中只是定义如何连接、设置事务、SQL操作等,所以具体的操作,需要由每家数据库厂商提供对应的驱动来进行实现JDBC。

演变过程

那么具体是如何做到的呢?这就是接下来要来进行分析的DriverManager和Driver(驱动)所要做的事情了。

一般情况下,在应用程序中进行数据库连接,调用JDBC接口,首先要将特定厂商的JDBC驱动实现加载到系统内存中,然后供系统使用。基本结构图如下:

驱动加载入内存的过程

这里所谓的驱动,其实就是实现了java.sql.Driver接口的类。如:

Oracle的驱动类是 oracle.jdbc.driver.OracleDriver.class(此类可以在oracle提供的JDBC jar包中找到),此类实现了java.sql.Driver接口。

MySQL的驱动类是com.mysql.cj.jdbc.Driver.class类,此类实现了java.sql.Driver接口。

由于驱动本质上还是一个class,将驱动加载到内存和加载普通的class原理是一样的:使用Class.forName("driverName")。

以下是将常用的数据库驱动加载到内存中的代码:

			//加载Oracle数据库驱动
			Class.forName("oracle.jdbc.driver.OracleDriver");
			
			//加载SQL Server数据库驱动
			Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
			
			//加载MySQL 数据库驱动
			Class.forName("com.mysql.cj.jdbc.Driver");

			//加载MySQL 数据库驱动
			Class.forName("com.mysql.jdbc.Driver");

注意:Class.forName()将对应的驱动类加载到内存中,然后执行内存中的static静态代码段,代码段中,会创建一个驱动Driver的实例,放入DriverManager中,供DriverManager使用。

Oracle加载驱动的静态方法

例如,在使用Class.forName() 加载oracle的驱动oracle.jdbc.driver.OracleDriver时,会执行OracleDriver中的静态代码段,创建一个OracleDriver实例,然后调用DriverManager.registerDriver()注册:

	static {
		Timestamp localTimestamp = Timestamp.valueOf("2000-01-01 00:00:00.0");
		try {
			if (defaultDriver == null) {
				//创建一个OracleDriver实例,然后注册到DriverManager中
                                defaultDriver = new OracleDriver();
				DriverManager.registerDriver(defaultDriver);
			}
 
		} catch (RuntimeException localRuntimeException) {
		} catch (SQLException localSQLException) {
		}

MySQL加载驱动的静态方法

在MySQL中,如下所示:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            // 利用DriverManager来加载驱动
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

从上面来看的话,我们加载了Driver,程序就会自动调用DriverManager来自动注册一个驱动到DriverManager中来,由DriverManager来进行管理维护Driver。

利用CopyOnWriteArrayList集合来进行存储Driver(适用于读多写少的情况)

那么Driver到底做了些什么事情呢?

Driver的功能

首先应该想到的是,既然是驱动,就需要来连接数据库,不同厂商提供的数据库有各自的实现方式。

那么既然实现不同,那么就需要来做统一管理,这个时候接口的作用就体现出来了。

因为各个厂商实现方式不同,连接数据库的方式也不同,那么就需要针对性的来做连接。

我们猜测一下:肯定有连接协议方式、账号密码、自定义参数等等信息,Driver都得需要来支持。

首先看下在接口中的定义:java.sql.Driver接口规定了Driver应该具有以下功能:

其中:

acceptsURL

**acceptsURL(String url) ** :用来测试对指定的url,该驱动能否打开这个url连接。driver对自己能够连接的url会针对不同的数据库厂商针对自己的协议来做自定义校验,只有符合自己的协议形式的url才认为自己能够打开这个url,如果能够打开,返回true,反之,返回false;

Oracle校验URL

例如:oracle定义的自己的url协议如下:

jdbc:oracle:thin:@//<host>:<port>/ServiceName

jdbc:oracle:thin:@<host>:<port>:<SID>

Oracle自己的acceptsURL(String url)方法如下:

	public boolean acceptsURL(String paramString) {
		if (paramString.startsWith("jdbc:oracle:")) {
			return (oracleDriverExtensionTypeFromURL(paramString) > -2);
		}
 
		return false;
	}
 
	private int oracleDriverExtensionTypeFromURL(String paramString) {
		int i = paramString.indexOf(58) + 1;
 
		if (i == 0) {
			return -2;
		}
		int j = paramString.indexOf(58, i);
 
		if (j == -1) {
			return -2;
		}
		if (!(paramString.regionMatches(true, i, "oracle", 0, j - i))) {
			return -2;
		}
		++j;
 
		int k = paramString.indexOf(58, j);
 
		if (k == -1) {
			return -3;
		}
		String str = paramString.substring(j, k);
 
		if (str.equals("thin")) {
			return 0;
		}
		if ((str.equals("oci8")) || (str.equals("oci"))) {
			return 2;
		}
 
		return -3;
	}
 

由上可知oracle定义了自己应该接收什么类型的URL,自己能打开什么类型的URL连接(注意:这里acceptsURL(url)只会校验url是否符合协议,不会尝试连接判断url是否有效) 。拓展阅读:常用数据库 JDBC URL格式

MySQL校验URL

例如:mysql定义的自己的url协议如下:

jdbc:mysql://host:port/database?x=xx?y=yy

在MySQL中也有代码来判断:Type.isSupported方法中,用一个枚举的scheme来进行判断,下面摘一段过来:

....
jdbc:mysql:
jdbc:mysql:replication:
....

而对于我们输入的url来说,如:jdbc:mysql://127.0.0.1:3306/tb_test

都是以jdbc:mysql:开头的,所以是符合条件的

connect

connect(String url,Properties info)方法,创建Connection对象,用来和数据库的数据操作和交互,而Connection则是真正数据库操作的开始。

对于MySQL来说,在这里也对URL做了校验

    public java.sql.Connection connect(String url, Properties info) throws SQLException {

        try {
          	// 针对URL来做协议校验
            if (!ConnectionUrl.acceptsUrl(url)) {
                return null;
            }

            ConnectionUrl conStr = ConnectionUrl.getConnectionUrlInstance(url, info);
            // 根据不同的类型在创建不同类型的数据库连接
            switch (conStr.getType()) {
                case SINGLE_CONNECTION:
                    return com.mysql.cj.jdbc.ConnectionImpl.getInstance(conStr.getMainHost());

                case FAILOVER_CONNECTION:
                case FAILOVER_DNS_SRV_CONNECTION:
                    return FailoverConnectionProxy.createProxyInstance(conStr);

                case LOADBALANCE_CONNECTION:
                case LOADBALANCE_DNS_SRV_CONNECTION:
                    return LoadBalancedConnectionProxy.createProxyInstance(conStr);

                case REPLICATION_CONNECTION:
                case REPLICATION_DNS_SRV_CONNECTION:
                    return ReplicationConnectionProxy.createProxyInstance(conStr);

                default:
                    return null;
            }

        } catch (UnsupportedConnectionStringException e) {
            // when Connector/J can't handle this connection string the Driver must return null
            return null;

        } catch (CJException ex) {
            throw ExceptionFactory.createException(UnableToConnectException.class,
                    Messages.getString("NonRegisteringDriver.17", new Object[] { ex.toString() }), ex);
        }
    }

上面注释说的是,如果给定的不是MySQL指定的URL,那么将会返回null,如果是正常的,那么将会加载对应的驱动进行返回。

如果找到了对应的类型,那么将会来创建不同的Connection的实例对象。

手动加载驱动 Driver 并实例化进行数据库操作的例子

Oracle

	public static void driverTest(){
		try {
			//1.加载oracle驱动类,并实例化
			Driver driver = (Driver) Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
 
			//2.判定指定的URL oracle驱动能否接受(符合oracle协议规则)
			boolean flag = driver.acceptsURL("jdbc:oracle:thin:@127.0.0.1:1521:xe");
			//标准协议测试
			boolean standardFlag1 = driver.acceptsURL("jdbc:oracle:thin:@//<host>:<port>/ServiceName");
			boolean standardFlag2 = driver.acceptsURL("jdbc:oracle:thin:@<host>:<port>:<SID>");
			System.out.println("协议测试:"+flag+"\t"+standardFlag1+"\t"+standardFlag2);
			
			//3.创建真实的数据库连接:
			String  url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
			Properties props = new Properties();
			props.put("user", "louluan");
			props.put("password", "123456");
			Connection connection = driver.connect(url, props);
			//connection 对象用于数据库交互,代码省略。。。。。
			
		} catch (Exception e) {
			System.out.println("加载Oracle类失败!");
			e.printStackTrace();
		} finally{
			
		}
	}

MySQL

	public static void driverTest(){
        try {
            //1.加载oracle驱动类,并实例化
            Driver driver = (Driver) Class.forName("com.mysql.cj.jdbc.Driver").newInstance();

            //2.判定指定的URL mysql驱动能否接受(符合mysql协议规则 jdbc:mysql:)
            boolean flag = driver.acceptsURL("jdbc:mysql://127.0.0.1:3306/tb_test");
            //标准协议测试
            boolean standardFlag1 = driver.acceptsURL("jdbc:mysql://localhost:3306/database");
            boolean standardFlag2 = driver.acceptsURL("jdbc:mysql://localhost/database");
            System.out.println("协议测试:"+flag+"\t"+standardFlag1+"\t"+standardFlag2);

            //3.创建真实的数据库连接:
            String  url = "jdbc:mysql://127.0.0.1:3306/tb_test";
            Properties props = new Properties();
            props.put("user", "root");
            props.put("password", "root");
            Connection connection = driver.connect(url, props);
            //connection 对象用于数据库交互,代码省略。。。。。

        } catch (Exception e) {
            System.out.println("加载Oracle类失败!");
            e.printStackTrace();
        } finally{

        }
	}

存在现象,为什么connnect中可以放入url和对应的props呢?

properties是如何解析的?

properties既然要作为连接数据库时作为额外附带参数,如用户名+密码等等,是如何来进行操作的呢?

那么看一下MySQL中是如何操作的?

从com.mysql.cj.jdbc.NonRegisteringDriver#connect找到关键性代码

   public java.sql.Connection connect(String url, Properties info) throws SQLException {

        try {
            // 校验URL协议
            if (!ConnectionUrl.acceptsUrl(url)) {
                return null;
            }
			
            // 解析url和info信息来组成连接串信息
            ConnectionUrl conStr = ConnectionUrl.getConnectionUrlInstance(url, info);
            // 省略创建数据库连接代码

        } catch (UnsupportedConnectionStringException e) {
            // when Connector/J can't handle this connection string the Driver must return null
            return null;

        } catch (CJException ex) {
            throw ExceptionFactory.createException(UnableToConnectException.class,
                    Messages.getString("NonRegisteringDriver.17", new Object[] { ex.toString() }), ex);
        }
    }

看下com.mysql.cj.conf.ConnectionUrl#getConnectionUrlInstance是如何来判断的?

    public static ConnectionUrl getConnectionUrlInstance(String connString, Properties info) {
        if (connString == null) {
            throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.0"));
        }
        // 这里会将Properties中的信息拿出来进行拼接
        String connStringCacheKey = buildConnectionStringCacheKey(connString, info);
        ConnectionUrl connectionUrl;
		// ........ 省略代码
        return connectionUrl;
    }

看下具体的代码:

    private static String buildConnectionStringCacheKey(String connString, Properties info) {
        StringBuilder sbKey = new StringBuilder(connString);
        sbKey.append("\u00A7"); // Section sign.
        sbKey.append(
                info == null ? null : info.stringPropertyNames().stream().map(k -> k + "=" + info.getProperty(k)).collect(Collectors.joining(", ", "{", "}")));
        return sbKey.toString();
    }

将k和value进行连接,最终拼接成:jdbc:mysql://127.0.0.1:3306/mysql_index_test§{user=root, password=root}

我们的properties中可以写哪些参数呢?

具体可以参考PropertyKey这个枚举类

存在问题

上述的手动加载Driver并且获取连接的过程稍显笨拙:如果现在我们加载进来了多个驱动Driver,那么手动创建Driver实例,并根据URL进行创建连接就会显得代码杂乱无章,并且还容易出错,并且不方便管理。JDBC中提供了一个DriverManager角色,用来管理这些驱动Driver。

这就是为什么要引入DriverManager的原因,要统一来进行管理不同厂商提供的Driver驱动!

DriverManager的功能

事实上,一般我们操作Driver,获取Connection对象都是交给DriverManager统一管理的。

DriverManger可以注册和删除加载的驱动程序,可以根据给定的url获取符合url协议的驱动Driver或者是建立Conenction连接,进行数据库交互。

以下是DriverManager的关键方法摘要:

DriverManager 内部持有这些注册进来的驱动 Driver,由于这些驱动都是 java.sql.Driver 类型,那么怎样才能获得指定厂商的驱动Driver呢?

在一个项目中可以使用到不同类型的数据库,那么就需要有对应的驱动来进行支持。Java为了能够满足对不同数据库类型的支持,也为了能够统一管理。

利用Java内置的SPI机制来进行加载这些驱动

答案就在于: SPI技术

SPI技术

SPI全称为 (Service Provider Interface):是JDK内置的一种服务提供发现机制。

SPI是一种动态替换发现的机制,一种解耦非常优秀的思想。

SPI工作原理: 就是ClassPath路径下的META-INF/services文件夹中, 以接口的全限定名来命名文件名,文件里面写该接口的实现。然后再资源加载的方式,读取文件的内容(接口实现的全限定名), 然后再去加载类。

spi可以很灵活的让接口和实现分离, 让api提供者只提供接口, 第三方来实现。

所以我们可以在MySQL依赖中找到META-INF/services/java.sql.Driver,然后文件中的内容是:

com.mysql.cj.jdbc.Driver

参考连接:

https://huaweicloud.csdn.net/63874ededacf622b8df8a9b9.html?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2defaultCTRLISTactivity-1-123450610-blog-118364823.pc_relevant_multi_platform_whitelistv3&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2defaultCTRLISTactivity-1-123450610-blog-118364823.pc_relevant_multi_platform_whitelistv3&utm_relevant_index=1

java.sql.Driver接口规定了厂商实现该接口,并且定义自己的URL协议。厂商们实现的Driver接口通过acceptsURL(String url)来判断此url是否符合自己的协议,如果符合自己的协议,则可以使用本驱动进行数据库连接操作,查询驱动程序是否认为它可以打开到给定 URL 的连接。

在下面回来详细进行说明

使用DriverManager获取指定Driver

驱动加载如内存的过程章节中,演示了Class.forName("...Driver")的方式,将不同数据库厂商的驱动加载到DriverManager中的CopyOnWriteArrayList来进行保存。

对于驱动加载后,如何获取指定的驱动程序呢?这里,DriverManager的静态方法getDriver(String url)可以通过传递给的URL,返回可以打开此URL连接的Driver。

比如,我想获取oracle的数据库驱动,只需要传递形如jdbc:oracle:thin:@::或者jdbc:oracle:thin:@//:/ServiceName的参数给DriverManager.getDriver(String url)即可:

Driver oracleDriver =DriverManager.getDriver("jdbc:oracle:thin:@<host>:<port>:<SID>");

实际上,DriverManager.getDriver(String url)方法是根据传递过来的URL,遍历它维护的驱动Driver,依次调用驱动的Driver的acceptsURL(url),如果返回acceptsURL(url)返回true,则返回对应的Driver:

	public static Driver getDriver(String paramString) throws SQLException {
	
		//省略部分代码。。。。
      	// 获取得到已经注册的所有的驱动
		Iterator localIterator = registeredDrivers.iterator();
		//遍历注册的驱动
		while (localIterator.hasNext()) {
			DriverInfo localDriverInfo = (DriverInfo) localIterator.next();
			if (isDriverAllowed(localDriverInfo.driver, localClass))
				try {
					//如果对应驱动类accepsURL() 为true,返回对应的driver
                    // 不同驱动类的acceptsURL方法对URL来做校验,如果满足,说明当前存在对应的驱动
					if (localDriverInfo.driver.acceptsURL(paramString)) {
						//返回对应的driver
						return localDriverInfo.driver;
					}
				} catch (SQLException localSQLException) {
				}
			else
				println("    skipping: "+ localDriverInfo.driver.getClass().getName());
		}
      	// 遍历完成所有的驱动之后,还没有找到,那么就抛出异常,说明没有对应的驱动支持对应的URL协议
		throw new SQLException("No suitable driver", "08001");
		//-----省略部分代码
	}
为什么可以通过URL来获取得到Driver
    private static boolean isDriverAllowed(Driver var0, ClassLoader var1) {
        boolean var2 = false;
        if (var0 != null) {
            Class var3 = null;

            try {
              	// 得到对应的类,然后加载进来!!这里是类,而不是对象
                var3 = Class.forName(var0.getClass().getName(), true, var1);
            } catch (Exception var5) {
                var2 = false;
            }

            var2 = var3 == var0.getClass();
        }

        return var2;
    }

因为DriverManger就是为了统一加载各种Driver的,不如利用多态机制让各自的类来进行实现。

因为对于Oracle和MySQL来说,都有各自的驱动,这里通过SPI机制来进行加载。

使用DriverManager注册和取消注册驱动Driver

在开始的地方讨论了当使用Class.forName("driverName")加载驱动的时候,会向DriverManager中注册一个Driver实例。以下代码将验证此说法:

	public static void defaultDriver(){
		try {
			
			String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
			
			//1.将Driver加载到内存中,然后执行其static静态代码,创建一个OracleDriver实例注册到DriverManager中
			Class.forName("oracle.jdbc.driver.OracleDriver");
			//取出对应的oracle 驱动Driver
			Driver driver  = DriverManager.getDriver(url);
			System.out.println("加载类后,获取Driver对象:"+driver);
			
			//将driver从DriverManager中注销掉
			DriverManager.deregisterDriver(driver);
			//重新通过url从DriverManager中取Driver
			driver  = DriverManager.getDriver(url);
			System.out.println(driver);			
		} catch (Exception e) {
			System.out.println("加载Oracle类失败!");
			e.printStackTrace();
		} finally{
			
		}
	}

以上代码主要分以下几步:

  1. 首先是将 oracle.jdbc.driver.OracleDriver加载到内存中;
  2. 然后便调用DriverManager.getDriver()去取Driver实例;
  3. 将driver实例从DriverManager中注销掉;
  4. 尝试再取对应url的Driver实例

上述代码执行的结果如下:

从执行结果看,正好能够验证以上论述:当第四步再次获取对应url的 Driver 实例时,由于已经被注销掉了,找不到适当的驱动Driver,抛出了 "Not suitable driver" 的异常。

	public static void defaultDriver(){
		try {
			
			String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
			
			//1.将Driver加载到内存中,然后执行其static静态代码,创建一个OracleDriver实例注册到DriverManager中
			Driver dd = (Driver)Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
			//2.取出对应的oracle 驱动Driver
			Driver driver  = DriverManager.getDriver(url);
			System.out.println("加载类后,获取Driver对象:"+driver);
			
			//3. 将driver从DriverManager中注销掉
			DriverManager.deregisterDriver(driver);
			
			//4.此时DriverManager中已经没有了驱动Driver实例,将创建的dd注册到DriverManager中
			DriverManager.registerDriver(dd);
			
			//5.重新通过url从DriverManager中取Driver
			driver  = DriverManager.getDriver(url);
                       
            System.out.println("注销掉静态创建的Driver后,重新注册的Driver:    "+driver);
			System.out.println("driver和dd是否是同一对象:" +(driver==dd));
		} catch (Exception e) {
			System.out.println("加载Oracle类失败!");
			e.printStackTrace();
		} finally{
			
		}
	}

以下代码运行的结果:

以上代码先创建了一个Driver对象,在注销了DriverManager中由加载驱动过程中静态创建驱动之后,注册到系统中,现在DriverManager中对应url返回的Driver 即是在代码中创建的Driver对象。

使用DriverManager创建 Connection 连接对象

创建 Connection 连接对象,可以使用驱动Driver的 connect(url,props);也可以使用 DriverManager 提供的getConnection()方法。

此方法通过url自动匹配对应的驱动Driver实例,然后调用对应的connect方法返回Connection对象实例。

Driver driver  = DriverManager.getDriver(url);
Connection connection = driver.connect(url, props);

上述代码等价于:

Class.forName("oracle.jdbc.driver.OracleDriver");
Connection connection = DriverManager.getConnection(url, props);

DriverManager 初始化

DriverManager 作为 Driver 的管理器,它在第一次被使用的过程中(即在代码中第一次用到的时候),它会被加载到内存中,然后执行其定义的static静态代码段,在静态代码段中,有一个 loadInitialDrivers() 静态方法,用于加载配置在jdbc.drivers 系统属性内的驱动Driver,配置在jdbc.drivers 中的驱动driver将会首先被加载:

    static {
      	// 加载并初始化所有的Driver
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
        SET_LOG_PERMISSION = new SQLPermission("setLog");
        DEREGISTER_DRIVER_PERMISSION = new SQLPermission("deregisterDriver");
    }

可以看下如何来进行初始化的:

    private static void loadInitialDrivers() {
        String var0;
        try {
          	// 利用SPI机制的首选
            var0 = (String)AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                  	// 加载系统变量 jdbc.drivers 设置的冒号隔开的驱动
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch (Exception var8) {
            var0 = null;
        }

        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {              	
              	// 点进去看下,是固定目录:META-INF/services/
                // 加载固定的类到内存中来
                ServiceLoader var1 = ServiceLoader.load(Driver.class);
                Iterator var2 = var1.iterator();
                try {
                    while(var2.hasNext()) {
                        var2.next();
                    }
                } catch (Throwable var4) {
                }

                return null;
            }
        });
		// ........省略代码
    }
证明

在上面的过程中,使用Class.forName来进行加载驱动并将Driver注册到DriverManager中的。

但是现在看完了DriverManager的静态方法之后,也可以不使用Class.forName来加载驱动。

证明jdbc.drvers来获取当前项目中的驱动,首先利用maven项目引入oracle和MySQL对应的坐标

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc8 -->
        <dependency>
            <groupId>com.oracle.database.jdbc</groupId>
            <artifactId>ojdbc8</artifactId>
            <version>21.1.0.0</version>
        </dependency>

然后写一段代码:

    public static void main(String[] args) {
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()){
            Driver driver = drivers.nextElement();
            String driverName = driver.getClass().getName();
            System.out.println("获取得到驱动的名称--->"+driverName);
        }
    }

打印结果:

获取得到驱动的名称--->com.mysql.cj.jdbc.Driver
获取得到驱动的名称--->oracle.jdbc.OracleDriver

总结

1、JDBC针对不同的数据库厂商通过接口来约束对应的行为;比如说:校验URL是否符合厂商条件、连接时候对参数进行校验;

2、JDBC为了针对各种数据库厂商提供的驱动进行管理,引入了DriverManager类处理

3、JDBC利用SPI技术来加载管理维护不同数据库厂商的驱动;

重点理解上面的两个图即可:

posted @ 2022-12-18 17:24  写的代码很烂  阅读(348)  评论(0编辑  收藏  举报