摘自: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靜態代碼段只會被執行一次!