JDBC 4.2 Specifications 中文翻译 -- 第九章 连接
一個 Connection 對象,表示了與某個數據源的一條連接,數據源的種類可以是關系型數據庫,文件系統等等之類,只要有對應的 JDBC 驅動,都可以稱之為數據源。應用程序使用 JDBC API 來維護多條連接,這些連接可能訪問的是多個數據源,也可能訪問的只是一個數據源。從 JDBC 驅動的角度來看,一個 Connection 對象就意味著一個客戶端會話,一個會話會保持許多狀態,例如用戶 ID,一系列的 SQL Statement 以及結果集,也保存了當前使用的事務處理策略。
可以通過以下兩種方式之一來獲取一條連接:
- 使用 DriverManager 這個類以及各種各樣的驅動實現
- 使用 DataSource 類
更推薦使用 DataSource 對象來獲取連接,因為這增強了應用的可移植性,使得代碼更容易維護了,并且使得對連接池和分布式事務的使用更加地透明。所有的 Java EE 組件,都會使用 DataSource 對象來獲取連接。
這一章將會介紹各種不同的 JDBC 驅動以及如何使用 Driver 接口、DriverManager 類以及基本的 DataSource 接口。關于連接池和分布式事務的介紹分別在第11章和第12章做介紹。
9.1 驅動的種類
- Type 1
這種類型的 JDBC 驅動是對另外一種訪問 API 的映射,比如說 ODBC,一般需要依賴本地庫,這就導致了它的可移植性不行。JDBC-ODBC 橋就是這種類型的驅動。 - Type 2
這種類型的 JDBC 驅動一部分是用 Java 語言寫的,一部分是用本地代碼寫的。這種驅動使用一個本地的客戶端庫來連接數據源。由于對本地代碼的使用,可移植性也不行。 - Type 3
這種類型的驅動使用純 Java 語言編寫,但是通信的時候需要經過一個中間件,使用的是與數據庫具體協議無關的獨立協議。這個中間件轉發客戶端的請求給后面的數據源。 - Type 4
這種類型的驅動使用純 Java 語言編寫,并且使用網絡協議或者文件 IO 與具體的數據源通信,客戶端直接與數據源連接。
9.2 Driver 接口
編寫 JDBC 驅動,必須實現 Driver 接口,并且實現類中必須包含一個靜態初始化塊,當驅動被加載時,這塊代碼會被調用。這塊代碼的主要工作是講自己注冊給 DriverManager,如下代碼所示:
public class AcmeJdbcDriver implements java.sql.Driver {static {java.sql.DriverManager.registerDriver(newAcmeJdbcDriver());} }驅動的實現類必須提供一個無參構造函數,當 DriverManager 想要與 Driver 交互時,它會直接調用它的方法,Driver 接口包含了一個 acceptsURL 方法,DriverManager 可以調用這個方法來判斷該驅動是否能處理對應的 JDBC URL。
當 DriverManager 想要建立一條數據庫連接時,它會調用驅動實現類的 connect 方法,并把 URL 作為參數穿給它,這個方法會返回一個 Connection 對象,或者是當無法建立數據庫連接時,拋出一個 SQLException。如果驅動實現類無法解析 URL,這個方法將會返回 null。
## 9.2.1 加載一個實現了 java.sql.Driver 接口的驅動類
DriverManager 初始化的時候,會先通過 “java.drivers” 這個系統屬性來嘗試加載驅動,如以下例子:
DriverManager 的 getConnection 方法能夠支持 Java SE 的 SPI 服務發現機制,JDBC 4.0 的驅動必須包含以下文件 “META-INF/services/java.sql.Driver”,這個文件會包含實現了 Driver 接口的類名
9.3 DriverAction 接口
當 DriverManager 的 deregisterDriver 方法被調用時,如果想要被通知到,那么 JDBC 驅動就得有對應的實現了 DriverAction 接口的類,DriverAction 的具體實現類并不希望直接被上層應用拿來使用,所以實現 JDBC 驅動的時候,應該將這個類定義為私有的類,以防止被直接使用。
JDBC 驅動的靜態初始化塊里面,必須調用 DriverManager.registerDriver(java.sql.Driver, java.sql.DriverAction) 方法,這樣當一個 JDBC 驅動被 DriverManager 注銷的時候,才能被通知到,如下所示:
public class AcmeJdbcDriver implements java.sql.Driver {static DriverAction da;static {java.sql.DriverManager.registerDriver(newAcmeJdbcDriver(), da);} }9.4 DriverManager 類
DriverManager 類與 Driver 接口一起協作,維護所有可用的 JDBC 驅動。當應用程序通過一個 URL 來獲取一個連接的時候, DriverManager 負責找到一個適用該 URL 的驅動,用這個驅動來獲取對應的數據源的連接。
DriverManager 的關鍵方法如下所示:
- registerDriver
這個方法會將某個驅動加進可用驅動的集合里,它在一個驅動被裝載的時候隱式地調用,一般情況下是驅動的靜態代碼塊里調用這個方法。 - getConnection
這個方法用來獲取一個連接,要調用這個方法,必須提供一個 JDBC URL,DriverManager 會使用這個 URL 來輪詢所有已經注冊的驅動,并找到一個可以識別這個 URL 的驅動,驅動會返回一個 Connection 給 DriverManager,然后再把它交給應用程序。
JDBC URL 的格式如下所示:
jdbc:<subprotocol>:<subname>subprotocol 定義是要連接的是哪種類型的數據庫,subname 則會根據 subprotoco 的不同而不同。
以下代碼示范了如何從 DriverManager 獲取一個連接:
DriverManager 類也提供了另外一些獲取連接的方法:
- getConnection(String url)
這個方法適用于不需要提供用戶名和密碼的情況 - getConnection(String url, java.util.Properties prop)
這個方法允許在 prop 參數里加入用戶名和密碼,以及其它屬性
DriverPropertyInfo 這個類提供了一個驅動可以理解的所有的屬性,詳見 Java JDBC API DOC
9.5 SQLPermission 類
這個類代表了一個代碼基所擁有的權限。當前唯一定義的權限是 setLog 權限。當一個 Applet 調用了 DriverManager 的 setLogWriter 或者 setLogStream 方法時,SecurityManager 將會檢查是否有權限。如果沒有權限,將會拋出一個 java.lang.SecurityException 異常
9.6 DataSource 接口
DataSource 這個接口是在 JDBC2.0 的可選屬性里引進的,這是 JDBC 規范推薦的用來獲取數據源連接的方式。實現了 DataSource 接口的 JDBC 驅動會返回和通過 DriverManager 獲取的相同的 Connection 實例,使用 DataSource 接口使應用程序更加具有可移植性,因為應用程序不需要為某個特定的驅動提供相關的連接信息,僅僅需要提供一個邏輯的數據源名。邏輯數據源名用來映射到 JNDI 提供的 DataSource 實例。這個 DataSource 實例代表了一個物理上的數據源,并提供獲取相應連接的方法。如果關于數據源的屬性或者信息發生了變化,DataSource 對象可以感知到對應的變化,完全不需要改變應用代碼。
實現 DataSource 接口時,應該透明地提供以下功能:
- 通過連接的池化來提高性能和可擴展性
- 通過 XADataSource 接口來支持分布式事務
還需要注意的是,DataSource 的實現類必須提供一個無參構造函數
接下來的3個小節主要討論:
9.6.1 DataSource 的屬性
JDBC API 定義了一系列的屬性來描述 DataSource 的實現,具體的屬性有哪些,取決于具體的 DataSource 實現,也就是說,取決于該實現是一個基本的 DataSource 對象,還是 ConnectionPoolDataSource,或者是 XADataSource,無論什么實現,它們都會有共同的屬性 description,以下是標準的 DataSource 屬性:
| databaseName | String | 數據庫名 |
| dataSourceName | String | 數據源名,用來命名底層的 XADataSource 或者是 ConnectionPoolDataSource |
| description | String | 對此 DataSource 的描述信息 |
| networkProtocol | String | 網絡協議 |
| password | String | 數據庫密碼 |
| portNumber | int | 數據庫監聽端口 |
| roleName | String | 初始 SQL roleName |
| serverName | String | 數據庫服務器名 |
| user | String | 數據庫用戶名 |
DataSource 的屬性遵循 JavaBean 1.01 規范。具體 DataSource 實現可以添加屬性,但是不能與原有的有沖突。這些屬性必須提供對應的 setter 和 getter 方法,當一個新的 DataSource 初始化的時候,這些屬性也應該相應進行初始化,如以下代碼所示,這里的實現是一個 VendorDataSource:
VendorDataSource vds = new VendorDataSource(); vds.setServerName("my_database_server"); String name = vds.getServerName();DataSource 的屬性,設計的初衷是不應該直接被應用代碼獲取,應該在具體的實現類里提供獲取的方法,而不是在 DataSource 上定義 public 的屬性,想要獲取屬性值,可以通過“自省”的方式(反射)來獲取。
9.6.2 JNDI API 以及應用可移植性
Java Naming and Directory Interface (JNDI) API 提供讓應用通過網絡訪問遠程服務的統一方式,本小節將描述如何使用 JNDI 來注冊并訪問一個 JDBC 數據源對象。更詳細的信息可以查閱 JNDI 規范。
使用 JNDI API,應用可以通過指定一個邏輯名來訪問一個數據源,在這里 JNDI 需要使用到命名服務,來將邏輯名映射到對應的數據源。這個特性極大地增強了應用的可移植性,因為很多數據源的配置,可以在不修改應用層代碼的情況下進行修改,例如端口號和服務器名。事實上,應用可以透明地訪問另一個完全不同的數據源,只需要修改對應的配置。在三層架構的環境中,這個特性很重要,應用服務器會將訪問不同數據源的細節隱藏起來,不需要對應用開放。
以下代碼實例了如何使用 JNDI 來注冊一個數據源對象:
// Create a VendorDataSource object and set some properties VendorDataSource vds = new VendorDataSource(); vds.setServerName("my_database_server"); vds.setDatabaseName("my_database"); vds.setDescription("data source for inventory and personnel");// Use the JNDI API to register the new VendorDataSource object. // Reference the root JNDI naming context and then bind the // logical name "jdbc/AcmeDB" to the new VendorDataSource object. Context ctx = new InitialContext(); ctx.bind("jdbc/AcmeDB", vds);9.6.3 通過 DataSource 實例獲取連接
一旦一個 DataSource 注冊在 JNDI 的命名服務后,應用可以使用它來獲取一條到物理數據源的連接,如下代碼所示:
// Get the initial JNDI naming context Context ctx = new InitialContext(); // Get the DataSource object associated with the logical name // "jdbc/AcmeDB" and use it to obtain a database connection DataSource ds = (DataSource)ctx.lookup("jdbc/AcmeDB"); Connection con = ds.getConnection("user", "pwd");9.6.4 關閉連接
Connection.close(), Connection.isclosed() 和 Connection.isValid() 這些方法可以用來關閉一條連接和判斷一條連接是否還處于活躍狀態。
9.6.4.1 Connection.close
An application calls the method當應用使用完一條連接后,可以調用 Connection.close() 來關閉這條連接,在這條連接上所有的 Statement 對象也會被關閉。
一條連接關閉后,除了 close(), isClosed() 和 isValid() 方法外,調用其它的方法將會拋出一個 SQLException。
9.6.4.2 Connection.isClosed
這個方法用來判斷一條連接的 close() 方法是否已經被調用過,這個方法不能用來判斷連接是否還可用。
但是有寫 JDBC 驅動可能會增強 isClosed() 方法,使得可以利用這個方法來判斷一條連接是否還可用。在這里,為了最大的可移植性,應用應該通過 Connection.isValid() 來判斷一條連接是否還可用。
9.6.4.3 Connection.isValid
這個方法用來標識一條連接是否還可用,如果不可用,那么除了 close(),isClosed() 和isValid() 方法之外,調用其它方法將會拋出 SQLException
總結
以上是生活随笔為你收集整理的JDBC 4.2 Specifications 中文翻译 -- 第九章 连接的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: golang数据类型与MySQL数据类型
- 下一篇: Rhino脚本引擎技术介绍