JDBC驱动加载

概述
一般情况下,在应用程序中进行数据库连接,调用JDBC接口,首先要将特定厂商的JDBC驱动实现加载到系统内存中,然后供系统使用。基本结构图如下:
在这里插入图片描述
驱动加载入内存的过程
这里所谓的驱动,其实就是实现了java.sql.Driver接口的类。如oracle的驱动类是 oracle.jdbc.driver.OracleDriver.class(此类可以在oracle提供的JDBC jar包中找到),此类实现了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.jdbc.Driver");

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

例如,在使用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) { 
 }

Driver的功能
java.sql.Driver接口规定了Driver应该具有以下功能:在这里插入图片描述
其中:

acceptsURL(String url) 方法用来测试对指定的url,该驱动能否打开这个url连接。driver对自己能够连接的url会制定自己的协议,只有符合自己的协议形式的url才认为自己能够打开这个url,如果能够打开,返回true,反之,返回false;

例如: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是否有效) 。connect(String url,Properties info)方法,创建Connection对象,用来和数据库的数据操作和交互,而Connection则是真正数据库操作的开始(在此方法中,没有规定是否要进行acceptsURL()进行校验)。

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

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{ 
           
    } 
}

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

DriverManager角色
事实上,一般我们操作Driver,获取Connection对象都是交给DriverManager统一管理的。DriverManger可以注册和删除加载的驱动程序,可以根据给定的url获取符合url协议的驱动Driver或者是建立Conenction连接,进行数据库交互。在这里插入图片描述
下是DriverManager的关键方法摘要:在这里插入图片描述
DriverManager 内部持有这些注册进来的驱动 Driver,由于这些驱动都是 java.sql.Driver 类型,那么怎样才能获得指定厂商的驱动Driver呢?答案就在于:java.sql.Driver接口规定了厂商实现该接口,并且定义自己的URL协议。厂商们实现的Driver接口通过acceptsURL(String url)来判断此url是否符合自己的协议(这段话是作者所说,实际不一定是通过acceptURL方法判断,但逻辑顺序是对的,即先判断是否符合协议格式,再试图创建Connection实例),如果符合自己的协议,则可以使用本驱动进行数据库连接操作,查询驱动程序是否认为它可以打开到给定 URL 的连接。

使用DriverManager获取指定Driver
对于驱动加载后,如何获取指定的驱动程序呢?这里,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:@::”);
实际上,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 
                if (localDriverInfo.driver.acceptsURL(paramString)) { 
                    //返回对应的driver 
                    return localDriverInfo.driver; 
                } 
            } catch (SQLException localSQLException) { 
            } 
        else 
            println("    skipping: "+ localDriverInfo.driver.getClass().getName()); 
    } 
    throw new SQLException("No suitable driver", "08001"); 
    //-----省略部分代码 
}

使用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” 的异常。

将上述的例子稍作变化,在注销掉了静态块创建的driver后,往DriverManager注册一个自己创建的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);

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

static { 
    loadInitialDrivers();//加载配置在jdbc.drivers系统变量中的驱动driver 
    println("JDBC DriverManager initialized"); 
    SET_LOG_PERMISSION = new SQLPermission("setLog"); 
} 
 
 
private static void loadInitialDrivers() { 
    String str1; 
    try { 
        str1 = (String) AccessController 
                .doPrivileged(new PrivilegedAction() { 
                    public String run() { 
                        return System.getProperty("jdbc.drivers");//返回jdbc.drivers值 
                    } 
                }); 
    } catch (Exception localException1) { 
        str1 = null; 
    } 
   
               //省略部分代码...... 
    if ((str1 == null) || (str1.equals(""))) 
        return; 
    String[] arrayOfString1 = str1.split(":"); 
    println("number of Drivers:" + arrayOfString1.length); 
    for (String str2 : arrayOfString1) 
        try { 
            //Class.forName加载对应的driver 
            Class.forName(str2, true, ClassLoader.getSystemClassLoader()); 
        } catch (Exception localException2) { 
            println("DriverManager.Initialize: load failed: " 
                    + localException2); 
        } 
} 
String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe"; 
//设置值系统变量jdbc.drivers 
System.setProperty("jdbc.drivers", "oracle.jdbc.driver.OracleDriver"); 
//2.通过特定的url获取driver 
Driver driver = DriverManager.getDriver(url); 
//打印是否存在 
System.out.println(driver); 

请记住:一定要在第一次使用DriverManager之前设置jdbc.drivers,因为DriverManager中的static静态代码段只会被执行一次!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值