JDBC驅動加載


摘自:http://blog.csdn.net/luanlouis/article/details/29850811

概述       

      一般情況下,在應用程序中進行數據庫連接,調用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:@<host>:<port>:<SID>或者jdbc:oracle:thin:@//<host>:<port>/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  
                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靜態代碼段只會被執行一次!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM