年底Sun 公司發布了 Java Standard Edition (Java SE )的最終正式版代號 Mustang(野馬)跟 Tiger(Java SE )相比Mustang 在性能方面有了不錯的提升與 Tiger 在 API 庫方面的大幅度加強相比雖然 Mustang 在 API 庫方面的新特性顯得不太多但是也提供了許多實用和方便的功能在腳本WebServiceXML編譯器 API數據庫JMX網絡和 Instrumentation 方面都有不錯的新特性和功能加強 本系列 文章主要介紹 Java SE 在 API 庫方面的部分新特性通過一些例子和講解幫助開發者在編程實踐當中更好的運用 Java SE 提高開發效率
本文是系列文章的第 篇介紹了 Java SE 在數據庫編程方面的新特性
Java DBJava 裡的數據庫
新安裝了 JDK 的程序員們也許會發現除了傳統的 binjre 等目錄JDK 新增了一個名為 db 的目錄這便是 Java 的新成員Java DB這是一個純 Java 實現開源的數據庫管理系統(DBMS)源於 Apache 軟件基金會(ASF)名下的項目 Derby它只有 MB 大小對比動辄上 G 的數據庫來說可謂袖珍但這並不妨礙 Derby 功能齊備支持幾乎大部分的數據庫應用所需要的特性更難能可貴的是依托於 ASF 強大的社區力量Derby 得到了包括 IBM 和 Sun 等大公司以及全世界優秀程序員們的支持這也難怪 Sun 公司會選擇其 版本納入到 JDK 中作為內嵌的數據庫這就好像為 JDK 注入了一股全新的活力Java 程序員不再需要耗費大量精力安裝和配置數據庫就能進行安全易用標准並且免費的數據庫編程在這一章中我們將初窺 Java DB 的世界來探究如何使用它編寫出功能豐富的程序
Hello Java DB內嵌模式的Derby
既然有了內嵌(embedded)的數據庫就讓我們從一個簡單的范例(代碼在 清單 中列出)開始試著使用它吧這個程序做了大多數數據庫應用都可能會做的操作在 DBMS 中創建了一個名為 helloDB 的數據庫創建了一張數據表取名為 hellotable向表內插入了兩條數據然後查詢數據並將結果打印在控制台上最後刪除表和數據庫釋放資源
清單 HelloJavaDB 的代碼
public class HelloJavaDB {
public static void main(String[] args) {
try { // load the driver
ClassforName(orgapachederbyjdbcEmbeddedDriver)newInstance();
Systemoutprintln(Load the embedded driver);
Connection conn = null;
Properties props = new Properties();
propsput(user user); propsput(password user);
//create and connect the database named helloDB
conn=DriverManagergetConnection(jdbc:derby:helloDB;create=true props);
Systemoutprintln(create and connect to helloDB);
connsetAutoCommit(false);
// create a table and insert two records
Statement s = conncreateStatement();
sexecute(create table hellotable(name varchar() score int));
Systemoutprintln(Created table hellotable);
sexecute(insert into hellotable values(Ruth Cao ));
sexecute(insert into hellotable values (Flora Shi ));
// list the two records
ResultSet rs = sexecuteQuery(
SELECT name score FROM hellotable ORDER BY score);
Systemoutprintln(name\t\tscore);
while(rsnext()) {
StringBuilder builder = new StringBuilder(rsgetString());
builderappend(\t);
builderappend(rsgetInt());
Systemoutprintln(buildertoString());
}
// delete the table
sexecute(drop table hellotable);
Systemoutprintln(Dropped table hellotable);
rsclose();
sclose();
Systemoutprintln(Closed result set and statement);
mit();
connclose();
Systemoutprintln(Committed transaction and closed connection);
try { // perform a clean shutdown
DriverManagergetConnection(jdbc:derby:;shutdown=true);
} catch (SQLException se) {
Systemoutprintln(Database shut down normally);
}
} catch (Throwable e) {
// handle the exception
}
Systemoutprintln(SimpleApp finished);
}
}
隨後我們在命令行(本例為 Windows 平台當然其它系統下稍作改動即可)下鍵入以下命令
清單 運行 HelloJavaDB 命令
java –cp ;%JAVA_HOME%\db\lib\derbyjar HelloJavaDB
程序將會按照我們預想的那樣執行圖 是執行結果的一部分截屏
圖 HelloJavaDB 程序的執行結果
上述的程序和以往沒什麼區別不同的是我們不需要再為 DBMS 的配置而勞神因為 Derby 已經自動地在當前目錄下新建了一個名為 helloDB 的目錄來物理地存儲數據和日志需要做的只是注意命名問題在內嵌模式下驅動的名字應為 orgapachederbyjdbcEmbeddedDriver創建一個新數據庫時需要在協議後加入 create=true另外關閉所有數據庫以及 Derby 的引擎可以使用以下代碼
清單 關閉所有數據庫及 Derby 引擎
DriverManagergetConnection(jdbc:derby:;shutdown=true);
如果只想關閉一個數據庫那麼則可以調用
清單 關閉一個數據庫
DriverManagergetConnection(jdbc:derby:helloDB;shutdown=true );
這樣使用嵌入模式的 Derby 維護和管理數據庫的成本接近於 這對於希望專心寫代碼的人來說不失為一個好消息然而有人不禁要問既然有了內嵌模式為什麼大多數的 DBMS 都沒有采取這樣的模式呢?不妨做一個小實驗當我們同時在兩個命令行窗口下運行 HelloJavaDB 程序結果一個的結果與剛才一致而另一個卻出現了錯誤如 圖 所示
圖 內嵌模式的局限
錯誤的原因其實很簡單在使用內嵌模式時Derby 本身並不會在一個獨立的進程中而是和應用程序一起在同一個 Java 虛擬機(JVM)裡運行因此Derby 如同應用所使用的其它 jar 文件一樣變成了應用的一部分這就不難理解為什麼在 classpath 中加入 derby 的 jar 文件我們的示例程序就能夠順利運行了這也說明了只有一個 JVM 能夠啟動數據庫而兩個跑在不同 JVM 實例裡的應用自然就不能夠訪問同一個數據庫了
鑒於上述的局限性和來自不同 JVM 的多個連接想訪問一個數據庫的需求下一節將介紹 Derby 的另一種模式網絡服務器(Network Server)
網絡服務器模式
如上所述網絡服務器模式是一種更為傳統的客戶端/服務器模式我們需要啟動一個 Derby 的網絡服務器用於處理客戶端的請求不論這些請求是來自同一個 JVM 實例還是來自於網絡上的另一台機器同時客戶端使用 DRDA(Distributed Relational Database Architecture)協議連接到服務器端這是一個由 The Open Group 倡導的數據庫交互標准圖 說明了該模式的大體結構
由於 Derby 的開發者們努力使得網絡服務器模式與內嵌模式之間的差異變小使得我們只需簡單地修改 清單 中的程序就可以實現如 清單 所示我們在 HelloJavaDB 中增添了一個新的函數和一些字符串變量不難看出新的代碼只是將一些在 上一節中特別指出的字符串進行了更改驅動類為 orgapachederbyjdbcClientDriver而連接數據庫的協議則變成了 jdbc:derby://localhost:/這是一個類似 URL 的字符串而事實上Derby 網絡的客戶端的連接格式為jdbc:derby://server[:port]/databaseName[;attributeKey=value]在這個例子中我們使用了最簡單的本地機器作為服務器而端口則是 Derby 默認的 端口
圖 Derby 網絡服務器模式架構
清單 網絡服務器模式下的 HelloJavaDB
public class HelloJavaDB {
public static String driver = orgapachederbyjdbcEmbeddedDriver;
public static String protocol = jdbc:derby:;
public static void main(String[] args) {
// same as before
}
private static void parseArguments(String[] args) {
if (argslength == || argslength > ) {
return;
}
if (args[]equalsIgnoreCase(derbyclient)) {
framework = derbyclient;
driver = orgapachederbyjdbcClientDriver;
protocol = jdbc:derby://localhost:/;
}
}
}
當然僅僅有客戶端是不夠的我們還需要啟動網絡服務器Derby 中控制網絡服務器的類是 orgapachederbydrdaNetworkServerControl因此鍵入以下命令即可如果想了解 NetworkServerControl 更多的選項只要把 start 參數去掉就可以看到幫助信息了關於網絡服務器端的實現都被 Derby 包含在 derbynetjar 裡
清單 啟動網絡服務器
java cp ;C:\Program Files\Java\jdk\db\lib\derbyjar;
C:\Program Files\Java\jdk\db\lib\derbynetjar
orgapachederbydrdaNetworkServerControl start
相對應的網絡客戶端的實現被包含在 derbyclientjar 中所以只需要在 classpath 中加入該 jar 文件修改後的客戶端就可以順利地讀取數據了再一次嘗試著使用兩個命令行窗口去連接數據庫就能夠得到正確的結果了如果不再需要服務器那麼使用 NetworkServerControl 的 shutdown 參數就能夠關閉服務器
更多
至此文章介紹了 Java SE 中的新成員Java DB(Derby)也介紹了如何在內嵌模式以及網絡服務器模式下使用 Java DB當然這只是淺嘗辄止更多高級的選項還需要在 Sun 和 Derby 的文檔中尋找在這一章的最後我們將簡單介紹幾個 Java DB 的小工具來加快開發速度它們都位於 orgapachederbytools 包內在開發過程中需要獲取信息或者測試可以用到
ij
一個用來運行 SQL 腳本的工具
dblook
為 Derby 數據庫作模式提取(Schema extraction)
生成 DDL 的工具
sysinfo
顯示系統以及 Derby 信息的工具類
JDBC 新功能新 API
如果說上一章介紹了 Java 中的一個新成員它本來就存在但是沒有被加入進 JDK那麼這一章我們將關注在 JDBC 中又增加了哪些新功能以及與之相對應的新 API
自動加載驅動
在 JDBC 之前編寫 JDBC 程序都需要加上以下這句有點丑陋的代碼
清單 注冊 JDBC 驅動
ClassforName(orgapachederbyjdbcEmbeddedDriver)newInstance();
JavasqlDriverManager 的內部實現機制決定了這樣代碼的出現只有先通過 ClassforName 找到特定驅動的 class 文件DriverManagergetConnection 方法才能順利地獲得 Java 應用和數據庫的連接這樣的代碼為編寫程序增加了不必要的負擔JDK 的開發者也意識到了這一點從 Java 開始應用程序不再需要顯式地加載驅動程序了DriverManager 開始能夠自動地承擔這項任務作為試驗我們可以將 清單 中的相關代碼刪除重新編譯後在 JRE 下運行結果和原先的程序一樣
好奇的讀者也許會問DriverManager 為什麼能夠做到自動加載呢?這就要歸功於一種被稱為 Service Provider 的新機制熟悉 Java 安全編程的程序員可能對其已經是司空見慣而它現在又出現在 JDBC 模塊中JDBC 的規范規定所有 JDBC 的驅動 jar 文件必須包含一個 javasqlDriver它位於 jar 文件的 METAINF/services 目錄下這個文件裡每一行便描述了一個對應的驅動類其實編寫這個文件的方式和編寫一個只有關鍵字(key)而沒有值(value)的 properties 文件類似同樣地#之後的文字被認為是注釋有了這樣的描述DriverManager 就可以從當前在 CLASSPATH 中的驅動文件中找到它應該去加載哪些類而如果我們在 CLASSPATH 裡沒有任何 JDBC 的驅動文件的情況下調用 清單 中的代碼會輸出一個 sunjdbcodbcJdbcOdbcDriver 類型的對象而仔細浏覽 JDK 的目錄這個類型正是在 %JAVA_HOME%/jre/lib/resourcesjar 的 METAINF/services 目錄下的 javasqlDriver 文件中描述的也就是說這是 JDK 中默認的驅動而如果開發人員想使得自己的驅動也能夠被 DriverManager 找到只需要將對應的 jar 文件加入到 CLASSPATH 中就可以了當然對於那些 JDBC 之前的驅動文件我們還是只能顯式地去加載了
清單 羅列本地機器上的 JDBC 驅動
Enumeration<Driver> drivers = DriverManagergetDrivers();
while(drivershasMoreElements()) {
Systemoutprintln(driversnextElement());
}
RowId
熟悉 DBOracle 等大型 DBMS 的人一定不會對 ROWID 這個概念陌生它是數據表中一個隱藏的列是每一行獨一無二的標識表明這一行的物理或者邏輯位置由於 ROWID 類型的廣泛使用Java SE 中新增了 javasqlRowId 的數據類型允許 JDBC 程序能夠訪問 SQL 中的 ROWID 類型誠然不是所有的 DBMS 都支持 ROWID 類型即使支持不同的 ROWID 也會有不同的生命周期因此使用 DatabaseMetaDatagetRowIdLifetime 來判斷類型的生命周期不失為一項良好的實踐經驗我們在 清單 的程序獲得連接之後增加以下代碼便可以了解 ROWID 類型的支持情況
清單
了解 ROWID 類型的支持情況
DatabaseMetaData meta = conngetMetaData();
Systemoutprintln(metagetRowIdLifetime());
Java SE 的 API 規范中javasqlRowIdLifetime 規定了 種不同的生命周期ROWID_UNSUPPORTEDROWID_VALID_FOREVERROWID_VALID_OTHERROWID_VALID_SESSION 和 ROWID_VALID_TRANSACTION從字面上不難理解它們表示了不支持 ROWIDROWID 永遠有效等等具體的信息還可以參看相關的 JavaDoc讀者可以嘗試著連接 Derby 進行試驗會發現運行結果是 ROWID_UNSUPPORTED 即 Derby 並不支持 ROWID
既然提供了新的數據類型那麼一些相應的獲取更新數據表內容的新 API 也在 Java 中被添加進來和其它已有的類型一樣在得到 ResultSet 或者 CallableStatement 之後調用 get/set/update 方法得到/設置/更新 RowId 對象示例的代碼如 清單 所示
清單 獲得/設置 RowId 對象
// Initialize a PreparedStatement
PreparedStatement pstmt = connectionprepareStatement(
SELECT rowid name score FROM hellotable WHERE rowid = ?);
// Bind rowid into prepared statement
pstmtsetRowId( rowid);
// Execute the statement
ResultSet rset = pstmtexecuteQuery();
// List the records
while(rsnext()) {
RowId id = rsgetRowId(); // get the immutable rowid object
String name = rsgetString();
int score = rsgetInt();
}
鑒於不同 DBMS 的不同實現RowID 對象通常在不同的數據源(datasource)之間並不是可移植的因此 JDBC 的 API 規范並不建議從連接 A 取出一個 RowID 對象將它用在連接 B 中以避免不同系統的差異而帶來的難以解釋的錯誤而至於像 Derby 這樣不支持 RowId 的 DBMS程序將直接在 setRowId 方法處拋出 SQLFeatureNotSupportedException
SQLXML
SQL 標准引入了 SQL/XML作為 SQL 標准的擴展SQL/XML 定義了 SQL 語言怎樣和 XML 交互如何創建 XML 數據如何在 SQL 語句中嵌入 XQuery 表達式等等作為 JDBC 的一部分Java 增加了 javasqlSQLXML 的類型JDBC 應用程序可以利用該類型初始化讀取存儲 XML 數據javasqlConnectioncreateSQLXML 方法就可以創建一個空白的 SQLXML 對象當獲得這個對象之後便可以利用 setStringsetBinaryStreamsetCharacterStream 或者 setResult 等方法來初始化所表示的 XML 數據以 setCharacterStream 為例清單 表示了一個 SQLXML 對象如何獲取 javaioWriter 對象從外部的 XML 文件中逐行讀取內容從而完成初始化
清單 利用 setCharacterStream 方法來初始化 SQLXML 對象
SQLXML xml = concreateSQLXML();
Writer writer = xmlsetCharacterStream();
BufferedReader reader = new BufferedReader(new FileReader(testxml));
String line= null;
while((line = readerreadLine() != null) {
writerwrite(line);
}
由於 SQLXML 對象有可能與各種外部的資源有聯系並且在一個事務中一直持有這些資源為了防止應用程序耗盡資源Java 提供了 free 方法來釋放其資源類似的設計在 javasqlArrayClob 中都有出現
至於如何使用 SQLXML 與數據庫進行交互其方法與其它的類型都十分相似可以參照 RowId 一節 中的例子在 Java SE 的 API 規范中找到 SQLXML 中對應的 get/set/update 方法構建類似的程序此處不再贅述
SQLExcpetion 的增強
在 Java SE 之前有關 JDBC 的異常類型不超過 個這似乎已經不足以描述日漸復雜的數據庫異常情況因此Java SE 的設計人員對以 javasqlSQLException 為根的異常體系作了大幅度的改進首先SQLException 新實現了 Iterable<Throwable> 接口清單 實現了 清單 程序的異常處理機制這樣簡潔地遍歷了每一個 SQLException 和它潛在的原因(cause)
清單 SQLException 的 foreach loop
// Java code
catch (Throwable e) {
if (e instanceof SQLException) {
for(Throwable ex : (SQLException)e ){
Systemerrprintln(extoString());
}
}
}
此外圖 表示了全部的 SQLException 異常體系除去原有的 SQLException 的子類Java 中新增的異常類被分為 種SQLReoverableExceptionSQLNonTransientExceptionSQLTransientException在 SQLNonTransientException 和 SQLTransientException 之下還有若干子類詳細地區分了 JDBC 程序中可能出現的各種錯誤情況大多數子類都會有對應的標准 SQLState 值很好地將 SQL 標准和 Java 類庫結合在一起
圖 SQLException 異常體系
在眾多的異常類中比較常見的有 SQLFeatureNotSupportedException用來表示 JDBC 驅動不支持某項 JDBC 的特性例如在 Derby 下運行 清單 中的程序就可以發現 Derby 的驅動並不支持 RowId 的特性另外值得一提的是SQLClientInfoException 直接繼承自 SQLException表示當一些客戶端的屬性不能被設置在一個數據庫連接時所發生的異常
小結更多新特性與展望
在本文中我們已經向讀者介紹了 Java SE 中 JDBC 最重要的一些新特性它們包括嵌在 JDK 中的 Java DB (Derby)和 JDBC 的一部分當然還有很多本文還沒有覆蓋到的新特性比如增加了對 SQL 語言中 NCHARNVARCHARLONGNVARCHAR 和 NCLOB 類型的支持在數據庫連接池的環境下為管理 Statement 對象提供更多靈活便利的方法等
此外在 Java SE 的 beta 版中曾經將 Annotation Query 的特性包含進來這項特性定義了一系列 Query 和 DataSet 接口程序員可以通過撰寫一些 Annotation 來自定義查詢並獲得定制的數據集結果但是由於這一特性的參考實現最終不能滿足 JDK 的質量需求Sun 公司忍痛割愛取消了在 Java SE 中發布其的計劃我們有理由相信在以後的 JDK 版本中這一特性以及更多新的功能將被包含進來利用 Java 語言構建數據庫的應用也會變得更為自然順暢
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19343.html