概述 Java 數(shù)據(jù)庫連接 3.0 規(guī)范的新功能和改進(jìn)之處
Josh Heidebrecht 軟件工程師, IBM 2001 年 7 月 04 日
Java 數(shù)據(jù)庫連接(Java Database Connectivity,JDBC)API 是作為 Java 2 標(biāo)準(zhǔn)版(Java 2 Standard Edition,J2SE)和 Java 2 企業(yè)版(Java 2 Enterprise Edition,J2EE)平臺(tái)的一個(gè)關(guān)鍵部分出現(xiàn)的。它是一種主要的基于標(biāo)準(zhǔn)的機(jī)制,能讓 Java 語言通過編程來訪問關(guān)系數(shù)據(jù)庫,所以當(dāng) Java Community Process 發(fā)布一份新版本的規(guī)范時(shí),開發(fā)人員一定會(huì)感興趣。在此,我們就 Sun Microsystems 最近發(fā)布的 JDBC 規(guī)范的提議最終草案(Proposed Final Draft)3.0 版本來總結(jié)一下它的一些新的主要功能。加入 討論論壇,與作者和其他讀者分享您對(duì)本文的看法。
介紹 Java 數(shù)據(jù)庫連接(JDBC)3.0 規(guī)范建立在其原本穩(wěn)固的基礎(chǔ)上,增加了幾個(gè)新功能以彌補(bǔ)原來功能不足的地方。無論是 java.sql 還是第一次出現(xiàn)的 javax.sql 軟件包,都會(huì)包含在還處于測(cè)試階段的 Java 1.4 版平臺(tái)中。在今年晚些時(shí)候它就會(huì)被正式發(fā)布,到時(shí) Java 開發(fā)人員就能夠利用這些改進(jìn)了,所以現(xiàn)在正是開始了解這些改變的好時(shí)候。
我們會(huì)簡(jiǎn)單地討論一下 JDBC 的設(shè)計(jì)師們?yōu)檫@個(gè)版本所考慮到的幾個(gè)設(shè)計(jì)目標(biāo)。理解了設(shè)計(jì)師們的設(shè)計(jì)基本原理,我們就可以更好地去理解那些改變。我們會(huì)總結(jié)一下規(guī)范中的幾個(gè)新功能以便了解整個(gè) API 是怎樣被改變的。另外,我們還會(huì)深入研究幾個(gè)最適用于應(yīng)用程序開發(fā)人員的關(guān)鍵功能,以成功地協(xié)助您利用其新性能。
設(shè)計(jì)目標(biāo) 設(shè)計(jì) JDBC 3.0 規(guī)范的初衷主要是讓原先的 JDBC 規(guī)范下的功能更加完美。因此,這個(gè)新規(guī)范的設(shè)計(jì)指導(dǎo)原則之一就是要與現(xiàn)存的應(yīng)用程序和驅(qū)動(dòng)程序保持兼容性。所以,JDBC 2 的用戶可以放心,他們的應(yīng)用程序能在 JDBC 3.0 下正確運(yùn)行。另外,使用以前那些遭反對(duì)的方法寫進(jìn) JDBC 1 API 的代碼也可以繼續(xù)運(yùn)行。
隨著 J2EE 平臺(tái)迅速的日益流行,設(shè)計(jì)師們也想增強(qiáng) JDBC 的可伸縮性。新增的語句池和增強(qiáng)的連接池支持離實(shí)現(xiàn)這個(gè)目標(biāo)還很遠(yuǎn)。此外,設(shè)計(jì)師們還仔細(xì)地考慮 JDBC 與新的連接器體系結(jié)構(gòu)之間的關(guān)系,來繼續(xù)提高服務(wù)器上的 Java 技術(shù)。
在 JDBC 2 開發(fā)的過程中,SQL99 還處在一種變化不定的情況下?,F(xiàn)在規(guī)范已經(jīng)完成了,而且數(shù)據(jù)庫廠商已經(jīng)采用了部分標(biāo)準(zhǔn)。所以自然地,JDBC 規(guī)范就跟著將自己與 SQL99 功能的一部分相統(tǒng)一。最新的 JDBC 規(guī)范已經(jīng)采用了 SQL99 標(biāo)準(zhǔn)中那些已經(jīng)被廣泛支持的功能,還有那些在五年內(nèi)可能會(huì)獲得支持的功能。
如果一個(gè)數(shù)據(jù)庫還不支持 JDBC 3.0 所支持的部分 SQL99 功能,驅(qū)動(dòng)程序可以使用元數(shù)據(jù) API 向應(yīng)用程序開發(fā)人員表明:其底層數(shù)據(jù)庫不支持一部分 JDBC 功能。這一點(diǎn)允許數(shù)據(jù)庫廠商生產(chǎn)出相應(yīng)的 JDBC 驅(qū)動(dòng)程序,盡管他們可能不支持所有的功能。增加的兩種新的數(shù)據(jù)類型以及對(duì)事務(wù)的 Savepoint 的支持說明了兩個(gè)和 SQL99 有關(guān)的改變。
新功能摘要
元數(shù)據(jù) API 元數(shù)據(jù) API 已經(jīng)得到更新, DatabaseMetaData 接口現(xiàn)在可以檢索 SQL 類型的層次結(jié)構(gòu),一種新的 ParameterMetaData 接口可以描述 PreparedStatement 對(duì)象中參數(shù)的類型和屬性。
CallableStatements 中已命名的參數(shù) 在 JDBC 3.0 之前,設(shè)置一個(gè)存儲(chǔ)過程中的一個(gè)參數(shù)要指定它的索引值,而不是它的名稱。 CallableStatement 接口已經(jīng)被更新了,現(xiàn)在您可以用名稱來指定參數(shù)。
數(shù)據(jù)類型的改變 JDBC 所支持的數(shù)據(jù)類型作了幾個(gè)改變,其中之一是增加了兩種新的數(shù)據(jù)類型。
為了便于修改 CLOB(Character Large OBject,字符型巨對(duì)象)、BLOB(Binary Large OBject,二進(jìn)制巨對(duì)象)和 REF(SQL 結(jié)構(gòu))類型的值,同名的數(shù)據(jù)類型接口都被更新了。接下來的是,因?yàn)槲覀儸F(xiàn)在能夠更新這些數(shù)據(jù)類型的值,所以 ResultSet 接口也被修改了,以支持對(duì)這些數(shù)據(jù)類型的列的更新,也包括對(duì) ARRAY 類型的更新。
增加的兩種新的數(shù)據(jù)類型是 java.sql.Types.DATALINK 和 java.sql.Types.BOOLEAN 。新增的數(shù)據(jù)類型指的是同名的 SQL 類型。DATALINK 提供對(duì)外部資源的訪問或 URL,而 BOOLEAN 類型在邏輯上和 BIT 類型是等同的,只是增加了在語義上的含義。DATALINK 列值是通過使用新的 getURL() 方法從 ResultSet 的一個(gè)實(shí)例中檢索到的,而 BOOLEAN 類型是通過使用 getBoolean() 來檢索的。
檢索自動(dòng)產(chǎn)生的關(guān)鍵字 為了解決對(duì)獲取自動(dòng)產(chǎn)生的或自動(dòng)增加的關(guān)鍵字的值的需求,JDBC 3.0 API 現(xiàn)在將獲取這種值變得很輕松。要確定任何所產(chǎn)生的關(guān)鍵字的值,只要簡(jiǎn)單地在語句的 execute() 方法中指定一個(gè)可選的標(biāo)記,表示您有興趣獲取產(chǎn)生的值。您感興趣的程度可以是 Statement.RETURN_GENERATED_KEYS ,也可以是 Statement.NO_GENERATED_KEYS 。在執(zhí)行這條語句后,所產(chǎn)生的關(guān)鍵字的值就會(huì)通過從 Statement 的實(shí)例方法 getGeneratedKeys() 來檢索 ResultSet 而獲得。 ResultSet 包含了每個(gè)所產(chǎn)生的關(guān)鍵字的列。清單 1 中的示例創(chuàng)建一個(gè)新的作者并返回對(duì)應(yīng)的自動(dòng)產(chǎn)生的關(guān)鍵字。 清單 1. 檢索自動(dòng)產(chǎn)生的關(guān)鍵字
Statement stmt = conn.createStatement();
// Obtain the generated key that results from the query.
stmt.executeUpdate("INSERT INTO authors " +
‘(first_name, last_name) " +
"VALUES (‘George‘, ‘Orwell‘)",
Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stmt.getGeneratedKeys();
if ( rs.next() ) {
// Retrieve the auto generated key(s).
int key = rs.getInt();
}
|
連接器關(guān)系 大多數(shù)應(yīng)用程序開發(fā)人員不需要知道 JDBC 和 J2EE 連結(jié)器體系結(jié)構(gòu)之間的關(guān)系,就可以很好地使用 JDBC API。但是,由于 JDBC 3.0 規(guī)范已經(jīng)考慮到這項(xiàng)新的體系結(jié)構(gòu),這使得開發(fā)人員能更好地理解 JDBC 在哪里適合 J2EE 標(biāo)準(zhǔn),以及這個(gè)規(guī)范的發(fā)展方向是什么。
J2EE 連結(jié)器體系結(jié)構(gòu)指定了一組協(xié)議,允許企業(yè)的信息系統(tǒng)以一種可插入的方式連接到應(yīng)用服務(wù)器上。這種體系結(jié)構(gòu)定義了負(fù)責(zé)與外部系統(tǒng)連接的資源適配器。連接器服務(wù)提供者接口(The Connectors Service Provider Interface,SPI)恰好和 JDBC 接口提供的服務(wù)緊密配合。
JDBC API 實(shí)現(xiàn)了連結(jié)器體系結(jié)構(gòu)定義的三個(gè)協(xié)議中的兩個(gè)。第一個(gè)是將應(yīng)用程序組件與后端系統(tǒng)相連接的連接管理,它是由 DataSource 和 ConnectionPoolDataSource 接口來實(shí)現(xiàn)的。第二個(gè)是支持對(duì)資源的事務(wù)性訪問的事務(wù)管理,它是由 XADataSource 來處理的。第三個(gè)是支持后端系統(tǒng)的安全訪問的安全性管理,在這點(diǎn)上,JDBC 規(guī)范并沒有任何對(duì)應(yīng)點(diǎn)。盡管有最后那個(gè)不足,JDBC 接口仍能映射到連接器 SPI 上。如果一個(gè)驅(qū)動(dòng)程序廠商將其 JDBC 驅(qū)動(dòng)程序映射到連接器系統(tǒng)協(xié)議上,它就可以將其驅(qū)動(dòng)程序部署為資源適配器,并立刻享受可插性、封裝和在應(yīng)用服務(wù)器中部署的好處。這樣,一個(gè)標(biāo)準(zhǔn)的 API 就可以在不同種類的的企業(yè)信息系統(tǒng)中,供企業(yè)開發(fā)人員使用。
ResultSet 可保持性 一個(gè)可保持的游標(biāo)(或結(jié)果),就是說該游標(biāo)在包含它的事務(wù)被提交后,也不會(huì)自動(dòng)地關(guān)閉。JDBC 3.0 增加了對(duì)指定游標(biāo)可保持性的支持。要制定您 ResultSet 的可保持性,您必須在使用 createStatement() 、 prepareStatement() 或 prepareCall() 方法準(zhǔn)備編寫一條語句時(shí)就這么做??杀3中钥梢允窍旅娉A恐械囊粋€(gè)。
HOLD_CURSORS_OVER_COMMIT |
ResultSet 對(duì)象(游標(biāo))沒有被關(guān)閉;它們?cè)谔峤徊僮鞯玫斤@式的或隱式的執(zhí)行以后仍保持打開的狀態(tài)。 |
CLOSE_CURSORS_AT_COMMIT |
ResultSet 對(duì)象(游標(biāo))在提交操作得到顯式的或隱式的執(zhí)行后被關(guān)閉。 |
總的來說,在事務(wù)提交之后關(guān)閉游標(biāo)操作會(huì)帶來更好的性能。除非您在事務(wù)結(jié)束后還需要該游標(biāo),否則您最好在執(zhí)行提交操作后將其關(guān)閉。因?yàn)橐?guī)范沒有規(guī)定 ResultSet 的缺省的可保持性,所以具體行為還將取決于執(zhí)行情況。然而,我希望在可以使用 JDBC 3.0 驅(qū)動(dòng)程序時(shí),大多數(shù)執(zhí)行在事務(wù)結(jié)束后仍舊會(huì)關(guān)閉游標(biāo)。
返回多重結(jié)果 JDBC 2 規(guī)范的一個(gè)局限是,在任意時(shí)刻,返回多重結(jié)果的語句只能打開一個(gè) ResultSet 。作為 JDBC 3.0 規(guī)范中改變的一個(gè)部分,規(guī)范將允許 Statement 接口支持多重打開的 ResultSets 。然而,重要的是 execute() 方法仍然會(huì)關(guān)閉任何以前 execute() 調(diào)用中打開的 ResultSet 。所以,要支持多重打開的結(jié)果, Statement 接口就要加上一個(gè)重載的 getMoreResults() 方法。新式的方法會(huì)做一個(gè)整數(shù)標(biāo)記,在 getResultSet() 方法被調(diào)用時(shí)指定前一次打開的 ResultSet 的行為。接口將按如下所示定義標(biāo)記:
CLOSE_ALL_RESULTS |
當(dāng)調(diào)用 getMoreResults() 時(shí),所有以前打開的 ResultSet 對(duì)象都將被關(guān)閉。 |
CLOSE_CURRENT_RESULT |
當(dāng)調(diào)用 getMoreResults() 時(shí),當(dāng)前的 ResultSet 對(duì)象將被關(guān)閉。 |
KEEP_CURRENT_RESULT |
當(dāng)調(diào)用 getMoreResults() 時(shí),當(dāng)前的 ResultSet 對(duì)象將不會(huì)被關(guān)閉。 |
清單 2 展示的是一個(gè)處理多重打開結(jié)果的示例。 清單 2. 如何處理多重打開結(jié)果
String procCall;
// Set the value of procCall to call a stored procedure.
// ...
CallableStatement cstmt = connection.prepareCall(procCall);
int retval = cstmt.execute();
if (retval == false) {
// The statement returned an update count, so handle it.
// ...
} else { // ResultSet
ResultSet rs1 = cstmt.getResultSet();
// ...
retval = cstmt.getMoreResults(Statement.KEEP_CURRENT_RESULT);
if (retval == true) {
ResultSet rs2 = cstmt.getResultSet();
// Both ResultSets are open and ready for use.
rs2.next();
rs1.next();
// ...
}
}
|
連接池 JDBC 3.0 定義了幾個(gè)標(biāo)準(zhǔn)的連接池屬性。開發(fā)人員并不需要直接地用 API 去修改這些屬性,而是通過應(yīng)用服務(wù)器或數(shù)據(jù)存儲(chǔ)設(shè)備來實(shí)現(xiàn)。由于開發(fā)人員只會(huì)間接地被連接池屬性的標(biāo)準(zhǔn)化所影響,所以有利之處并不明顯。然而,通過減少廠商特定設(shè)置的屬性的數(shù)量并用標(biāo)準(zhǔn)化的屬性來代替它們,開發(fā)人員能更容易地在不同廠商的 JDBC 驅(qū)動(dòng)程序之間進(jìn)行交換。另外,這些屬性還允許管理員很好地優(yōu)化連接池,從而使應(yīng)用程序的性能特點(diǎn)發(fā)揮到極致。這些屬性如下表所示。
| 屬性名稱 |
描述 |
maxStatements |
連接池可以保持打開的語句數(shù)目。 |
initialPoolSize |
當(dāng)池初始化時(shí)可以建立的物理連接的數(shù)目。 |
minPoolSize |
池可以包含的物理連接的最小數(shù)目。 |
maxPoolSize |
池可以包含的物理連接的最大數(shù)目。零指沒有最大值。 |
maxIdleTime |
持續(xù)時(shí)間,以秒計(jì),指一個(gè)閑置的物理連接在被關(guān)閉前可以在池中停留的時(shí)間。零指沒有限制。 |
propertyCycle |
間隔時(shí)間,以秒計(jì),指連接池在執(zhí)行其屬性策略前可以等待的時(shí)間。 |
預(yù)備語句池 除了改進(jìn)對(duì)連接池的支持以外,現(xiàn)在也能緩沖預(yù)備語句了。預(yù)備語句允許您用一條常用的 SQL 語句然后預(yù)編譯它,從而在這條語句被多次執(zhí)行的情況下大幅度地提升性能。在另一個(gè)方面,建立一個(gè) PreparedStatement 對(duì)象會(huì)帶來一定量的系統(tǒng)開銷。所以,在理想情況下,這條語句的生命周期應(yīng)該足夠長(zhǎng),以補(bǔ)償它所帶來的系統(tǒng)開銷。追求性能的開發(fā)人員有時(shí)候?yàn)榱搜娱L(zhǎng) PreparedStatement 對(duì)象的生命周期會(huì)不惜扭曲他們的對(duì)象模型。JDBC 3.0 讓開發(fā)人員不再為此擔(dān)心,因?yàn)閿?shù)據(jù)源層現(xiàn)在負(fù)責(zé)為預(yù)備語句進(jìn)行緩存。
清單 3 將示范如何利用 JDBC 對(duì)預(yù)備語句池的支持。細(xì)心的讀者可能會(huì)發(fā)現(xiàn)清單中的語句和普通 JDBC 2 的代碼沒什么兩樣。這是因?yàn)檎Z句的緩沖是完全在內(nèi)部實(shí)現(xiàn)的。這就意味著,在 JDBC 3.0 下,您現(xiàn)存的代碼可以自動(dòng)利用語句池。但可惜的是,這也意味著您將不能控制哪個(gè)預(yù)備語句將被緩沖,而只能控制被緩存的語句的數(shù)目。 清單 3. 緩沖預(yù)備語句
String INSERT_BOOK_QUERY = "INSERT INTO BOOKLIST " +
‘(AUTHOR, TITLE) " +
"VALUES (?, ?) ";
Connection conn = aPooledConnection.getConnection();
PreparedStatement ps = conn.prepareStatement(INSERT_BOOK_QUERY);
ps.setString(1, "Orwell, George");
ps.setString(2, "1984");
ps.executeUpdate();
ps.close();
conn.close();
// ...
conn = aPooledConnection.getConnection();
// Since the connection is from a PooledConnection, the data layer has
// the option to retrieve this statement from its statement pool,
// saving the VM from re-compiling the statement again.
PreparedStatement cachedStatement = conn.prepareStatemet(INSERT_BOOK_QUERY);
// ...
|
在您的事務(wù)中使用 Savepoint 也許在 JDBC 3.0 中最令人興奮的附加特點(diǎn)就是 Savepoint 了。JDBC 2 中的事務(wù)支持讓開發(fā)人員可以控制對(duì)數(shù)據(jù)的并發(fā)訪問,從而保證持續(xù)數(shù)據(jù)總是保持一致的狀態(tài)。可惜的是,有時(shí)候需要的是對(duì)事務(wù)多一點(diǎn)的控制,而不是在當(dāng)前的事務(wù)中簡(jiǎn)單地對(duì)每一個(gè)改變進(jìn)行回滾。在 JDBC 3.0 下,您就可以通過 Savepoint 獲得這種控制。 Savepoint 接口允許您將事務(wù)分割為各個(gè)邏輯斷點(diǎn),以控制有多少事務(wù)需要回滾。圖 1 將說明如何在事務(wù)中運(yùn)用 Savepoint。
圖 1. Savepoint 的直觀表示
您或許不是經(jīng)常需要使用 Savepoint。然而,在一種普遍的情況下 Savepoint 會(huì)發(fā)揮作用,那就是您需要作一系列的改變,但是在知道所有的結(jié)果之前不能確定應(yīng)該保留這些改變的哪一部分。清單 4 中的代碼示例說明了如何使用 Savepoint 接口。 清單 4. 使用 Savepoint
conn.setAutoCommit(false);
// Set a conservative transaction isolation level.
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
Statement stmt = conn.createStatement();
int rows = stmt.executeUpdate( "INSERT INTO authors " +
‘(first_name, last_name) VALUES " +
‘(‘Lewis‘, ‘Carroll‘)");
// Set a named savepoint.
Savepoint svpt = conn.setSavepoint("NewAuthor");
// ...
rows = stmt.executeUpdate( "UPDATE authors set type = ‘fiction‘ " +
"WHERE last_name = ‘Carroll‘");
// ...
conn.rollback(svpt);
// ...
// The author has been added, but not updated.
conn.commit();
|
結(jié)論 JDBC 3.0 現(xiàn)在正在測(cè)試期中,官方發(fā)行定在 2001 年下半年。主要的數(shù)據(jù)庫廠商正在致力于提供 JDBC 3.0 的驅(qū)動(dòng)程序,一些早期的測(cè)試版驅(qū)動(dòng)程序已經(jīng)可以獲得。JDBC 3.0 的改變雖然在本質(zhì)上不是革命性的,但也是一個(gè)非常重要的進(jìn)步。通過在現(xiàn)有功能上的擴(kuò)展,新的 JDBC 規(guī)范帶給您的是新的策略,以解決您的關(guān)系數(shù)據(jù)庫的問題。
|