Java SE 6.0版以兼容性、穩(wěn)定性和品質(zhì)作為設(shè)計(jì)目標(biāo)。本版本中有不少值得關(guān)注的增強(qiáng)特性,特別是JMX、web services、腳本語言支持(采用Rhino腳本引擎JSR 223把JavaScript技術(shù)與Java源碼進(jìn)行集成)、數(shù)據(jù)庫連接、支持annotations和安全部分。另外,在JDBC API中還有不少新的特性,包括RowId支持和新增的SQLException子類。
JDBC 4.0的特性
得益于Mustang中的Java SE 服務(wù)提供商機(jī)制,Java開發(fā)人員再也不必用類似Class.forName()的代碼注冊JDBC驅(qū)動來明確加載JDBC。當(dāng)調(diào)用DriverManager.getConnection()方法時(shí),DriverManager類將自動設(shè)置合適的驅(qū)動程序。該特性向后兼容,因此無需對現(xiàn)有的JDBC代碼作任何改動。
通過對Java應(yīng)用程序訪問數(shù)據(jù)庫代碼的簡化,使得JDBC 4.0有更好的開發(fā)體驗(yàn)。JDBC 4.0同時(shí)也提供了工具類來改進(jìn)數(shù)據(jù)源和連接對象的管理,也改進(jìn)了JDBC驅(qū)動加載和卸載機(jī)制。
有了JDBC 4.0傳承自Java SE 5.0 (Tiger)版對元數(shù)據(jù)的支持功能,Java開發(fā)人員可用Annotations明確指明SQL查詢。基于標(biāo)注的SQL查詢允許我們通過在Java代碼中使用Annotation關(guān)鍵字正確指明SQL查詢字符串。這樣,我們不必查看JDBC代碼和他所調(diào)用的數(shù)據(jù)庫兩份不同的文件。例如,用一個名為getActiveLoans()方法在貸款處理數(shù)據(jù)庫中獲取一個活躍貸款清單,你可以添加@Query(sql="SELECT * FROM LoanApplicationDetails WHERE LoanStatus = 'A'")標(biāo)注來修飾該方法。
并且,最終版的Java SE 6開發(fā)包(JDK 6)以及其相應(yīng)的執(zhí)行期環(huán)境(JRE 6)會捆綁一個基于Apache Derby的數(shù)據(jù)庫。這使得Java開發(fā)人員無需下載、安裝和配置一款單獨(dú)的數(shù)據(jù)庫產(chǎn)品就能探究JDBC的新特性。
JDBC 4.0中增加的主要特性包括:
- 1. JDBC驅(qū)動類的自動加載
- 2. 連接管理的增強(qiáng)
- 3. 對RowId SQL類型的支持
- 4. SQL的DataSet實(shí)現(xiàn)使用了Annotations
- 5. SQL異常處理的增強(qiáng)
- 6. 對SQL XML的支持
另外,對BLOB/CLOB 的改進(jìn)支持以及對國際字符集的支持也是JDBC 4.0的特性。這些特性將在隨后章節(jié)中詳細(xì)討論。JDBC驅(qū)動自動加載
在JDBC 4.0中,我們不必再使用Class.forName()方法明確加載JDBC驅(qū)動。當(dāng)調(diào)用getConnection方法時(shí),DriverManager會嘗試從初始化時(shí)已經(jīng)加載的JDBC驅(qū)動程序庫中選擇合適的驅(qū)動,以及他在當(dāng)前應(yīng)用的同一個類加載器中明確加載使用過的驅(qū)動。
DriverManager中的getConnection和getDrivers方法已作了改進(jìn),以支持Java SE 服務(wù)提供商機(jī)制(SPM)。根據(jù)SPM,所謂服務(wù)就是一組廣為人知的接口和抽象類的集合,而服務(wù)提供商就是對某一服務(wù)的特定實(shí)現(xiàn)。SPM還指明了服務(wù)提供商的配置文件存放于META-INF/services目錄下。JDBC 4.0的驅(qū)動程序庫必須包含META-INF/services/java.sql.Driver文件。該文件包含對java.sql.Driver 實(shí)現(xiàn)的JDBC驅(qū)動文件名。例如,通過JDBC驅(qū)動連接Apache Derby數(shù)據(jù)庫,META-INF/services/java.sql.Driver將含有以下路徑:
org.apache.derby.jdbc.EmbeddedDriver
我們再來快速地看一下如何使用這一新特性加載一個JDBC驅(qū)動管理。以下顯示的是我們用以加載JDBC驅(qū)動的典型范例代碼。這里我們假設(shè)連接的是Apache Derby數(shù)據(jù)庫,因?yàn)樵摂?shù)據(jù)庫將在本文隨后的范例應(yīng)用中用到:
??? Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); ??? Connection conn = ??????? DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword); |
但在JDBC 4.0中,我們不必寫Class.forName()這一行,我們只需要調(diào)用getConnection()方法取得數(shù)據(jù)庫連接。
請注意,僅在完全獨(dú)立的模式下可使用該方法取得數(shù)據(jù)庫的連接。如果你使用一些類似數(shù)據(jù)庫連接池等技術(shù)管理連接,那么代碼將有所不同。
連接管理
在JDBC 4.0之前,我們依靠JDBC URL來定義一個數(shù)據(jù)源連接?,F(xiàn)在有了JDBC 4.0,我們只需為標(biāo)準(zhǔn)連接工廠機(jī)制提供一組參數(shù),就能獲取與任何數(shù)據(jù)源的連接。Connection和Statement接口增添的新方法為池環(huán)境中管理Statement對象提供了更好的連接狀態(tài)跟蹤機(jī)制和更大的靈活度。元數(shù)據(jù)工具(JSR-175)被用來管理活躍連接。我們還能獲得元數(shù)據(jù)信息,如:活躍連接狀態(tài),并能指明XA事務(wù)的某一連接是標(biāo)準(zhǔn)式(Connection,在獨(dú)立應(yīng)用的情況下)、池式(PooledConnection)還是分布式(XAConnection)。該接口僅在諸如WebLogic、WebSphere和JBoss等Java EE應(yīng)用服務(wù)器的事務(wù)管理中使用。RowId支持
JDBC 4.0增加了RowID接口以支持ROWID數(shù)據(jù)類型,Oracle和DB2數(shù)據(jù)庫支持該數(shù)據(jù)類型。在你需要把大量缺少唯一標(biāo)識符字段的查詢記錄放入一個不允許重復(fù)對象的Collection容器(如Hashtable)的情況下,RowId很有用。我們可以用ResultSet的getRowId()方法獲取RowId,用PreparedStatement的setRowId()方法在查詢中使用RowId。
關(guān)于RowId對象需要記住的一件重要事項(xiàng)是,RowId值在數(shù)據(jù)源之間不可移植。當(dāng)在PreparedStatement和ResultSet中單獨(dú)使用set或update方法時(shí),需要想到指明數(shù)據(jù)源。因此,RowId對象不應(yīng)被不同的Connection和ResultSet對象共享。
DatabaseMetaData中的getRowIdLifetime()方法可被用來確定RowId對象的有效存活時(shí)間。該方法的可能返回值如表1所列:
?RowId值 | ?描述 |
?ROWID_UNSUPPORTED | ?Doesn't support ROWID data type. |
?ROWID_VALID_OTHER | ?Lifetime of the RowID is dependent on database vendor implementation. |
?ROWID_VALID_TRANSACTION | ?Lifetime of the RowID is within the current transaction as long as the row in the database table is not deleted. |
?ROWID_VALID_SESSION | ?Lifetime of the RowID is the duration of the current session as long as the row in the database table is not deleted. |
?ROWID_VALID_FOREVER | ?Lifetime of the RowID is unlimited as long as the row in the database table is not deleted. |
基于標(biāo)注的SQL查詢
JDBC 4.0對標(biāo)注(Java SE 5新增)作了進(jìn)一步規(guī)范和補(bǔ)充,他允許開發(fā)人員不必再寫大量代碼就能達(dá)到聯(lián)系SQL查詢和Java類的目的。并且,通過使用Generics(JSR 014)和元數(shù)據(jù)(JSR 175) API,我們只要指明查詢的輸入和輸出參數(shù)就能將SQL查詢與Java對象進(jìn)行關(guān)聯(lián)。我們還可以將查詢結(jié)果捆綁在Java類上以加快查詢輸出的處理。將查詢對象置于Java對象之中,我們可以不必像往常一樣寫所有代碼。在Java代碼中指明SQL查詢,經(jīng)常用到兩種標(biāo)注:
Select標(biāo)注
Select標(biāo)注用于在Java類中指明一個選擇查詢,使get方法能從數(shù)據(jù)庫表中取回?cái)?shù)據(jù)。表2顯示的是Select標(biāo)注的不同屬性及其作用:
?變量 | ?類型 | ?描述 |
?sql | ?String | ?SQL Select query string. |
?value | ?String | ?Same as sql attribute. |
?tableName | ?String | ?Name of the database table against which the sql will be invoked. |
?readOnly, connected, scrollable | ?Boolean | ?Flags used to indicate if the returned DataSet is read-only or updateable, is connected to the back-end database, and is scrollable when used in connected mode respectively. |
?allColumnsMapped | ?Boolean | ?Flag to indicate if the column names in the sql annotation element are mapped 1-to-1 with the fields in the DataSet. |
? 這里有一個應(yīng)用Select標(biāo)注從貸款數(shù)據(jù)庫中獲取所有活躍貸款的例子:
interface LoanAppDetailsQuery extends BaseQuery { ??????? @Select("SELECT * FROM LoanDetais where LoanStatus = 'A'") ??????? DataSet<LoanApplication> getAllActiveLoans(); } |
Sql標(biāo)注同樣允許I/O參數(shù)(參數(shù)標(biāo)記以一個問號后跟一個整型數(shù)據(jù)表示)。這里是一個參數(shù)化sql查詢的例子:
interface LoanAppDetailsQuery extends BaseQuery { ??????? @Select(sql="SELECT * from LoanDetails ??????????????? where borrowerFirstName= ?1 and borrowerLastName= ?2") ??????? DataSet<LoanApplication> getLoanDetailsByBorrowerName(String borrFirstName, ??????????????? String borrLastName); } |
Update標(biāo)注用于修飾Query接口方法以更新數(shù)據(jù)庫表中的一條或多條記錄。Update標(biāo)注必須包含一個sql標(biāo)注類型元素。這里是一個Update標(biāo)注的例子:
interface LoanAppDetailsQuery extends BaseQuery { |
SQL異常處理的增強(qiáng)特性
異常處理是Java編程的一個重要部分,特別是對后臺關(guān)系數(shù)據(jù)庫進(jìn)行連接或查詢時(shí)。SQLException是我們用以指出與數(shù)據(jù)庫相關(guān)錯誤的類。JDBC 4.0在SQLException處理中有不少增強(qiáng)。為了在處理SQLException時(shí)獲得更好的開發(fā)體驗(yàn),JDBC 4.0版作了如下增強(qiáng):
- 1. 新的SQLException子類
- 2. 對因果關(guān)系的支持
- 3. 對改進(jìn)的for-each循環(huán)的支持
新的SQLException類
SQLException的新子類提供了一種方法,使Java開發(fā)人員能寫出更方便改動的錯誤處理代碼。JDBC 4.0介紹了兩個新的SQLException型別:
- ? SQL non-transient exception
- ? SQL transient exception
Non-Transient Exception:除非修正引發(fā)SQLException異常的代碼,否則該異常在再次嘗試相同JDBC操作失敗后被拋出。表3顯示了JDBC 4.0新增的SQLNonTransientException異常子類(SQL 2003規(guī)范中定義了SQLState類的值):
?Exception class SQLState | ?value |
?SQLFeatureNotSupportedException | ?0A |
?SQLNonTransientConnectionException | ?08 |
?SQLDataException | ?22 |
?SQLIntegrityConstraintViolationException | ?23 |
?SQLInvalidAuthorizationException | ?28 |
?SQLSyntaxErrorException | ?42 |
? Transient Exception:當(dāng)先前執(zhí)行失敗的JDBC操作在沒有任何應(yīng)用級功能干涉的情況下可能成功執(zhí)行時(shí),該異常被拋出。繼承自SQLTransientException的新異常如表4所列:
?Exception class SQLState | ?value |
?SQLTransientConnectionException | ?08 |
?SQLTransactionRollbackException | ?40 |
?SQLTimeoutException | ?None |
因果關(guān)系
SQLException類現(xiàn)在支持Java SE鏈?zhǔn)疆惓C(jī)制(又稱因果工具),它使我們能在一個JDBC操作中處理多條SQLException異常(如果后臺數(shù)據(jù)庫支持多條異常特性)。這種情形發(fā)生在執(zhí)行一條可能會拋出多條SQLException異常的語句時(shí)。
我們可以調(diào)用SQLException中的getNextException()方法在異常鏈中進(jìn)行迭代。這里是一些處理getNextException()因果關(guān)系的范例代碼:
catch(SQLException ex) { ???? while(ex != null) { ??????? LOG.error("SQL State:" + ex.getSQLState()); ??????? LOG.error("Error Code:" + ex.getErrorCode()); ??????? LOG.error("Message:" + ex.getMessage()); ??????? Throwable t = ex.getCause(); ??????? while(t != null) { ??????????? LOG.error("Cause:" + t); ??????????? t = t.getCause(); ??????? } ??????? ex = ex.getNextException(); ??? } } |
Java SE 5中,SQLException類通過實(shí)現(xiàn)Iterable接口,增加了for-each循環(huán)支持的特性。這個循環(huán)的軌跡將會包括SQLException和其異常成因。這里的代碼片斷展示了SQLException中增加的增強(qiáng)型for-each環(huán)特性。
catch(SQLException ex) { ???? for(Throwable e : ex ) { ??????? LOG.error("Error occurred: " + e); ???? } } |
對國際字符集轉(zhuǎn)換的支持
以下是JDBC類處理國際字符集的新增特性:
- 1. JDBC數(shù)據(jù)類型:新增NCHAR、NVARCHAR、LONGNVARCHAR和NCLOB數(shù)據(jù)類型。
- 2. PreparedStatement:新增setNString、setNCharacterStream和setNClob方法。
- 3. CallableStatement:新增getNClob、getNString和getNCharacterStream方法。
- 4. ResultSet:ResultSet接口新增updateNClob、updateNString和updateNCharacterStream方法。
對大對象(BLOBs and CLOBs)支持的改進(jìn)
以下是JDBC 4.0處理LOBs的新增特性:
- 1. Connection:新增方法(createBlob()、createClob()和createNClob())以創(chuàng)建BLOB、CLOB和NCLOB對象新實(shí)例。
- 2. PreparedStatement:新增方法setBlob()、setClob()和setNClob()以使用InputStream對象插入BLOB對象,使用Reader對象插入CLOB和NCLOB對象。
- 3. LOBs:在Blob、Clob和NClob接口中新增方法(free())以釋放這些對象所持有的資源。
現(xiàn)在,讓我們來看一看java.sql和javax.jdbc包的新類,以及他們所提供的服務(wù)。
JDBC 4.0 API:新類
RowId (java.sql)
正如先前所介紹的,該接口是對數(shù)據(jù)庫中SQL ROWID值的展示。ROWID是一種SQL內(nèi)建的數(shù)據(jù)類型,用來標(biāo)識數(shù)據(jù)庫中的一行特定數(shù)據(jù)。ROWID經(jīng)常用于從表中返回查詢結(jié)果,而這些結(jié)果行往往缺少唯一ID字段。
getRowId和setRowId等CallableStatement、PreparedStatement和ResultSet接口的方法允許程序員訪問SQL ROWID值。RowId接口還提供了一個方法(叫g(shù)etBytes())把ROWID值作為一個byte型數(shù)組返回。DatabaseMetaData接口有一個名為getRowIdLifetime的新方法,用以確定某一RowId對象的存活時(shí)間。RowId的存活時(shí)間范圍可以是如下三種類型:
- 1. 創(chuàng)建RowId的數(shù)據(jù)庫事務(wù)持續(xù)時(shí)間
- 2. 創(chuàng)建RowId的會話持續(xù)時(shí)間
- 3. 數(shù)據(jù)庫對應(yīng)的記錄還未被刪除的持續(xù)時(shí)間
DataSet (java.sql)
DataSet接口提供了對執(zhí)行SQL Query后所返回?cái)?shù)據(jù)類型的安全檢查。DataSet還可以運(yùn)行在連接或非連接模式。在連接模式下,DataSet類似于ResultSet的功能;而在非連接模式下,他類似于CachedRowSet的功能。由于DataSet繼承自List接口,因此我們能對查詢返回的記錄行進(jìn)行迭代。
對已有的類,JDBC 4.0也新增了不少方法。比如,Connection新增了createSQLXML和createSQLXML方法,ResultSet新增了getRowId方法。
范例應(yīng)用
本文所示的范例應(yīng)用是一個貸款處理應(yīng)用軟件,他有一個貸款搜索頁面,用戶可以通過輸入貸款I(lǐng)D提交查詢表,以獲取貸款詳情。貸款搜索頁面調(diào)用一個控制器對象,而該對象又調(diào)用一個DAO對象訪問后臺數(shù)據(jù)庫,以取回貸款詳情。這些詳情包括貸款人、貸款數(shù)額、貸款截至日期等信息,并顯示在貸款詳情屏幕上。后臺數(shù)據(jù)庫中,我們有一個名為LoanApplicationDetails的表,來存儲貸款軟件的詳情。
該范例應(yīng)用的用例是通過指定貸款I(lǐng)D來獲取貸款詳情。當(dāng)貸款已經(jīng)登記并將抵押物與利息掛鉤后,貸款詳情就可以被獲取。表5顯示了貸款處理應(yīng)用軟件項(xiàng)目的詳情。
?Name | ?Value |
?Project Name | ?JdbcApp |
?Project Directory | ?c:\dev\projects\JdbcApp |
?DB Directory | ?c:\dev\dbservers\apache\derby |
?JDK Directory | ?c:\dev\java\jdk_1.6.0 |
?IDE Directory | ?c:\dev\tools\eclipse |
?Database | ?Apache Derby 10.1.2.1 |
?JDK | ?6.0 (beta 2 release) |
?IDE | ?Eclipse 3.1 |
?Unit | ?Testing JUnit 4 |
?Build | ?Ant 1.6.5 |
下表所列的是我們連接貸款詳情Apache Derby數(shù)據(jù)庫所需的JDBC參數(shù)。這些參數(shù)存放于一個名為derby.properties的文本文件中,并置于項(xiàng)目的etc/jdbc目錄下(見表6)。
?Name | ?Value |
?JDBC Driver File | ?LoanApp\META-INF\services\java.sql.driver |
?Driver | ?org.apache.derby.ClientDriver |
?URL | ?jdbc:derby:derbyDB |
?User Id | ?user1 |
?Password | ?user1 |
請注意:Apache Derby數(shù)據(jù)庫提供了兩種JDBC驅(qū)動:嵌入式驅(qū)動(org.apache.derby.jdbc.EmbeddedDriver)和客戶端/服務(wù)器驅(qū)動(org.apache.derby.jdbc.ClientDriver)。我在范例應(yīng)用中使用客戶端/服務(wù)器版驅(qū)動。
以下是使用ij工具來啟動Derby數(shù)據(jù)庫服務(wù)器并創(chuàng)建新數(shù)據(jù)庫的命令。
要啟動Derby網(wǎng)絡(luò)服務(wù)器,需開啟一個命令行窗口,并運(yùn)行如下命令(請根據(jù)你本機(jī)的環(huán)境改寫DERBY_INSTALL和JAVA_HOME環(huán)境變量)。
set DERBY_INSTALL=C:\dev\dbservers\db-derby-10.1.2.1-bin
cd %DERBY_INSTALL%\frameworks\NetworkServer\bin |
要連接數(shù)據(jù)庫服務(wù)器并創(chuàng)建測試數(shù)據(jù)庫,需開啟另一個命令行窗口并運(yùn)行以下命令。請確保DERBY_INSTALL和JAVA_HOME環(huán)境變量符合你本機(jī)的環(huán)境。
set JAVA_HOME=C:\dev\java\jdk1.6.0
%JAVA_HOME%\bin\java org.apache.derby.tools.ij |
測試
要編譯Java源代碼,classpath需包括derby.jar和junit4.jar文件,這兩個文件在項(xiàng)目的lib目錄下。Classpath還需包括etc、etc/jdbc和etc/log4j目錄,這樣應(yīng)用程序才能訪問JDBC屬性文件和Log4J配置文件。我創(chuàng)建了一個Ant構(gòu)建腳本(在JdbcApp/build目錄下)來自動完成編譯和打包Java源代碼的工作。
用于測試貸款詳情數(shù)據(jù)庫訪問對象的測試類名為LoanAppDetailsDAOTest。我們傳入貸款I(lǐng)D和貸款人參數(shù)就可以獲取貸款詳情。
以下部分顯示了JDBC 4.0中自動加載JDBC驅(qū)動和基于標(biāo)注的SQL查詢特性的代碼范例。
JDBC驅(qū)動的自動加載
BaseDAO抽象類有一個名為getConnection的方法用以獲得一個數(shù)據(jù)庫連接。以下代碼片斷顯示了該方法(注意,我們不必注冊JDBC驅(qū)動)。只要java.sql.Driver文件中有合適的驅(qū)動程序類名(org.apache.derby.jdbc.ClientDriver),JDBC驅(qū)動將被自動加載。
protected Connection getConnection() throws DAOException { ??????? // Load JDBC properties first ??????? if (jdbcUrl == null || jdbcUser == null || ??????????????????????? jdbcPassword == null) { ??????????????? loadJdbcProperties(); ??????? } ??????? // Get Connection ??????? Connection conn = null; ??????? try { ??????????????? conn = DriverManager.getConnection(jdbcUrl, jdbcUser, ??????????????????????????????? jdbcPassword); ??????? } catch (SQLException sqle) { ??????????????? throw new DAOException("Error in getting a DB connection.", ??????????????????????????????? sqle); ??????? } ??????? return conn; } |
SQL標(biāo)注
LoanAppDetailsQuery接口有標(biāo)注了的SQL查詢,用以獲取活躍貸款清單(criteria is loanstatus="A")和某貸款人的貸款詳情(在一個貸款人有多筆貸款的情況下)。在上文中,我們已經(jīng)了解過了這些SQL標(biāo)注。這里的范例代碼顯示了我們?nèi)绾问褂脴?biāo)注來調(diào)用已定義的SQL查詢。
public DataSet<LoanAppDetails> getAllActiveLoans() throws Exception {
public DataSet<LoanAppDetails> getLoanDetailsByBorrowerName( |
結(jié)論
JDBC 4.0在SQL方面為開發(fā)者提供了更好的開發(fā)體驗(yàn)。JDBC 4.0的另一個目標(biāo)是為API增加更豐富的工具以提供企業(yè)級JDBC特性管理JDBC資源。并且,JDBC 4.0 API還提供了JDBC驅(qū)動的移植方式,使其符合J2EE連接器架構(gòu)(JCA)規(guī)范。這為JDBC廠商提供了向JDBC連接器遷移的能力。在面向企業(yè)服務(wù)的架構(gòu)(SPA)中使用JDBC數(shù)據(jù)源,該移植方式很重要。在SOA中,JDBC數(shù)據(jù)源可被部署在另一個企業(yè)服務(wù)總線(ESB)架構(gòu)內(nèi),而不需要為JDBC數(shù)據(jù)源另寫一份ESB實(shí)現(xiàn)代碼。
版權(quán)聲明:Techtarget獲Matrix授權(quán)發(fā)布,如需轉(zhuǎn)載請聯(lián)系Matrix
作者:feichangcai;feichangcai
原文:http://www.matrix.org.cn/resource/article/2006-11-19/Mustang+JDBC_c8c66f03-77c2-11db-bdce-bdc029e475a1.html
凡是有該標(biāo)志的文章,都是該blog博主Caoer(草兒)原創(chuàng),凡是索引、收藏
、轉(zhuǎn)載請注明來處和原文作者。非常感謝。