Java SE 6.0
Java SE 6.0版以兼容性、穩定性和品質作為設計目標。本版本中有不少值得關注的增強特性,特別是JMX、web services、腳本語言支持(采用Rhino腳本引擎JSR 223把JavaScript技術與Java源碼進行集成)、數據庫連接、支持annotations和安全部分。另外,在JDBC API中還有不少新的特性,包括RowId支持和新增的SQLException子類。
JDBC 4.0的特性
得益于Mustang中的Java SE 服務提供商機制,Java開發人員再也不必用類似Class.forName()的代碼注冊JDBC驅動來明確加載JDBC。當調用DriverManager.getConnection()方法時,DriverManager類將自動設置合適的驅動程序。該特性向后兼容,因此無需對現有的JDBC代碼作任何改動。
通過對Java應用程序訪問數據庫代碼的簡化,使得JDBC 4.0有更好的開發體驗。JDBC 4.0同時也提供了工具類來改進數據源和連接對象的管理,也改進了JDBC驅動加載和卸載機制。
有了JDBC 4.0傳承自Java SE 5.0 (Tiger)版對元數據的支持功能,Java開發人員可用Annotations明確指明SQL查詢。基于標注的SQL查詢允許我們通過在Java代碼中使用Annotation關鍵字正確指明SQL查詢字符串。這樣,我們不必查看JDBC代碼和他所調用的數據庫兩份不同的文件。例如,用一個名為getActiveLoans()方法在貸款處理數據庫中獲取一個活躍貸款清單,你可以添加@Query(sql="SELECT * FROM LoanApplicationDetails WHERE LoanStatus = 'A'")標注來修飾該方法。
并且,最終版的Java SE 6開發包(JDK 6)以及其相應的執行期環境(JRE 6)會捆綁一個基于Apache Derby的數據庫。這使得Java開發人員無需下載、安裝和配置一款單獨的數據庫產品就能探究JDBC的新特性。
JDBC 4.0中增加的主要特性包括:
- 1. JDBC驅動類的自動加載
- 2. 連接管理的增強
- 3. 對RowId SQL類型的支持
- 4. SQL的DataSet實現使用了Annotations
- 5. SQL異常處理的增強
- 6. 對SQL XML的支持
另外,對BLOB/CLOB 的改進支持以及對國際字符集的支持也是JDBC 4.0的特性。這些特性將在隨后章節中詳細討論。JDBC驅動自動加載
在JDBC 4.0中,我們不必再使用Class.forName()方法明確加載JDBC驅動。當調用getConnection方法時,DriverManager會嘗試從初始化時已經加載的JDBC驅動程序庫中選擇合適的驅動,以及他在當前應用的同一個類加載器中明確加載使用過的驅動。
DriverManager中的getConnection和getDrivers方法已作了改進,以支持Java SE 服務提供商機制(SPM)。根據SPM,所謂服務就是一組廣為人知的接口和抽象類的集合,而服務提供商就是對某一服務的特定實現。SPM還指明了服務提供商的配置文件存放于META-INF/services目錄下。JDBC 4.0的驅動程序庫必須包含META-INF/services/java.sql.Driver文件。該文件包含對java.sql.Driver 實現的JDBC驅動文件名。例如,通過JDBC驅動連接Apache Derby數據庫,META-INF/services/java.sql.Driver將含有以下路徑:
org.apache.derby.jdbc.EmbeddedDriver
我們再來快速地看一下如何使用這一新特性加載一個JDBC驅動管理。以下顯示的是我們用以加載JDBC驅動的典型范例代碼。這里我們假設連接的是Apache Derby數據庫,因為該數據庫將在本文隨后的范例應用中用到:
??? Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); ??? Connection conn = ??????? DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
|
但在JDBC 4.0中,我們不必寫Class.forName()這一行,我們只需要調用getConnection()方法取得數據庫連接。
請注意,僅在完全獨立的模式下可使用該方法取得數據庫的連接。如果你使用一些類似數據庫連接池等技術管理連接,那么代碼將有所不同。
連接管理
在JDBC 4.0之前,我們依靠JDBC URL來定義一個數據源連接。現在有了JDBC 4.0,我們只需為標準連接工廠機制提供一組參數,就能獲取與任何數據源的連接。Connection和Statement接口增添的新方法為池環境中管理Statement對象提供了更好的連接狀態跟蹤機制和更大的靈活度。元數據工具(JSR-175)被用來管理活躍連接。我們還能獲得元數據信息,如:活躍連接狀態,并能指明XA事務的某一連接是標準式(Connection,在獨立應用的情況下)、池式(PooledConnection)還是分布式(XAConnection)。該接口僅在諸如WebLogic、WebSphere和JBoss等Java EE應用服務器的事務管理中使用。RowId支持
JDBC 4.0增加了RowID接口以支持ROWID數據類型,Oracle和DB2數據庫支持該數據類型。在你需要把大量缺少唯一標識符字段的查詢記錄放入一個不允許重復對象的Collection容器(如Hashtable)的情況下,RowId很有用。我們可以用ResultSet的getRowId()方法獲取RowId,用PreparedStatement的setRowId()方法在查詢中使用RowId。
關于RowId對象需要記住的一件重要事項是,RowId值在數據源之間不可移植。當在PreparedStatement和ResultSet中單獨使用set或update方法時,需要想到指明數據源。因此,RowId對象不應被不同的Connection和ResultSet對象共享。
DatabaseMetaData中的getRowIdLifetime()方法可被用來確定RowId對象的有效存活時間。該方法的可能返回值如表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. |
基于標注的SQL查詢
JDBC 4.0對標注(Java SE 5新增)作了進一步規范和補充,他允許開發人員不必再寫大量代碼就能達到聯系SQL查詢和Java類的目的。并且,通過使用Generics(JSR 014)和元數據(JSR 175) API,我們只要指明查詢的輸入和輸出參數就能將SQL查詢與Java對象進行關聯。我們還可以將查詢結果捆綁在Java類上以加快查詢輸出的處理。將查詢對象置于Java對象之中,我們可以不必像往常一樣寫所有代碼。在Java代碼中指明SQL查詢,經常用到兩種標注:
Select標注
Select標注用于在Java類中指明一個選擇查詢,使get方法能從數據庫表中取回數據。表2顯示的是Select標注的不同屬性及其作用:
?變量 |
?類型 |
?描述 |
?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. |
? 這里有一個應用Select標注從貸款數據庫中獲取所有活躍貸款的例子:
interface LoanAppDetailsQuery extends BaseQuery { ??????? @Select("SELECT * FROM LoanDetais where LoanStatus = 'A'") ??????? DataSet<LoanApplication> getAllActiveLoans(); }
|
Sql標注同樣允許I/O參數(參數標記以一個問號后跟一個整型數據表示)。這里是一個參數化sql查詢的例子:
interface LoanAppDetailsQuery extends BaseQuery { ??????? @Select(sql="SELECT * from LoanDetails ??????????????? where borrowerFirstName= ?1 and borrowerLastName= ?2") ??????? DataSet<LoanApplication> getLoanDetailsByBorrowerName(String borrFirstName, ??????????????? String borrLastName); }
|
Update標注
Update標注用于修飾Query接口方法以更新數據庫表中的一條或多條記錄。Update標注必須包含一個sql標注類型元素。這里是一個Update標注的例子:
interface LoanAppDetailsQuery extends BaseQuery { ??????? @Update(sql="update LoanDetails set LoanStatus = ?1 ??????????????? where loanId = ?2") ??????? boolean updateLoanStatus(String loanStatus, int loanId); }
|
SQL異常處理的增強特性
異常處理是Java編程的一個重要部分,特別是對后臺關系數據庫進行連接或查詢時。SQLException是我們用以指出與數據庫相關錯誤的類。JDBC 4.0在SQLException處理中有不少增強。為了在處理SQLException時獲得更好的開發體驗,JDBC 4.0版作了如下增強:
- 1. 新的SQLException子類
- 2. 對因果關系的支持
- 3. 對改進的for-each循環的支持
新的SQLException類
SQLException的新子類提供了一種方法,使Java開發人員能寫出更方便改動的錯誤處理代碼。JDBC 4.0介紹了兩個新的SQLException型別:
- ? SQL non-transient exception
- ? SQL transient exception
Non-Transient Exception:除非修正引發SQLException異常的代碼,否則該異常在再次嘗試相同JDBC操作失敗后被拋出。表3顯示了JDBC 4.0新增的SQLNonTransientException異常子類(SQL 2003規范中定義了SQLState類的值):
?Exception class SQLState |
?value |
?SQLFeatureNotSupportedException |
?0A |
?SQLNonTransientConnectionException |
?08 |
?SQLDataException |
?22 |
?SQLIntegrityConstraintViolationException |
?23 |
?SQLInvalidAuthorizationException |
?28 |
?SQLSyntaxErrorException |
?42 |
? Transient Exception:當先前執行失敗的JDBC操作在沒有任何應用級功能干涉的情況下可能成功執行時,該異常被拋出。繼承自SQLTransientException的新異常如表4所列:
?Exception class SQLState |
?value |
?SQLTransientConnectionException |
?08 |
?SQLTransactionRollbackException |
?40 |
?SQLTimeoutException |
?None |
因果關系
SQLException類現在支持Java SE鏈式異常機制(又稱因果工具),它使我們能在一個JDBC操作中處理多條SQLException異常(如果后臺數據庫支持多條異常特性)。這種情形發生在執行一條可能會拋出多條SQLException異常的語句時。
我們可以調用SQLException中的getNextException()方法在異常鏈中進行迭代。這里是一些處理getNextException()因果關系的范例代碼:
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(); ??? } }
|
增強的For-Each環
Java SE 5中,SQLException類通過實現Iterable接口,增加了for-each循環支持的特性。這個循環的軌跡將會包括SQLException和其異常成因。這里的代碼片斷展示了SQLException中增加的增強型for-each環特性。
catch(SQLException ex) { ???? for(Throwable e : ex ) { ??????? LOG.error("Error occurred: " + e); ???? } }
|
對國際字符集轉換的支持
以下是JDBC類處理國際字符集的新增特性:
- 1. JDBC數據類型:新增NCHAR、NVARCHAR、LONGNVARCHAR和NCLOB數據類型。
- 2. PreparedStatement:新增setNString、setNCharacterStream和setNClob方法。
- 3. CallableStatement:新增getNClob、getNString和getNCharacterStream方法。
- 4. ResultSet:ResultSet接口新增updateNClob、updateNString和updateNCharacterStream方法。
對大對象(BLOBs and CLOBs)支持的改進
以下是JDBC 4.0處理LOBs的新增特性:
- 1. Connection:新增方法(createBlob()、createClob()和createNClob())以創建BLOB、CLOB和NCLOB對象新實例。
- 2. PreparedStatement:新增方法setBlob()、setClob()和setNClob()以使用InputStream對象插入BLOB對象,使用Reader對象插入CLOB和NCLOB對象。
- 3. LOBs:在Blob、Clob和NClob接口中新增方法(free())以釋放這些對象所持有的資源。
現在,讓我們來看一看java.sql和javax.jdbc包的新類,以及他們所提供的服務。
JDBC 4.0 API:新類
RowId (java.sql)
正如先前所介紹的,該接口是對數據庫中SQL ROWID值的展示。ROWID是一種SQL內建的數據類型,用來標識數據庫中的一行特定數據。ROWID經常用于從表中返回查詢結果,而這些結果行往往缺少唯一ID字段。
getRowId和setRowId等CallableStatement、PreparedStatement和ResultSet接口的方法允許程序員訪問SQL ROWID值。RowId接口還提供了一個方法(叫getBytes())把ROWID值作為一個byte型數組返回。DatabaseMetaData接口有一個名為getRowIdLifetime的新方法,用以確定某一RowId對象的存活時間。RowId的存活時間范圍可以是如下三種類型:
- 1. 創建RowId的數據庫事務持續時間
- 2. 創建RowId的會話持續時間
- 3. 數據庫對應的記錄還未被刪除的持續時間
DataSet (java.sql)
DataSet接口提供了對執行SQL Query后所返回數據類型的安全檢查。DataSet還可以運行在連接或非連接模式。在連接模式下,DataSet類似于ResultSet的功能;而在非連接模式下,他類似于CachedRowSet的功能。由于DataSet繼承自List接口,因此我們能對查詢返回的記錄行進行迭代。
對已有的類,JDBC 4.0也新增了不少方法。比如,Connection新增了createSQLXML和createSQLXML方法,ResultSet新增了getRowId方法。
范例應用
本文所示的范例應用是一個貸款處理應用軟件,他有一個貸款搜索頁面,用戶可以通過輸入貸款ID提交查詢表,以獲取貸款詳情。貸款搜索頁面調用一個控制器對象,而該對象又調用一個DAO對象訪問后臺數據庫,以取回貸款詳情。這些詳情包括貸款人、貸款數額、貸款截至日期等信息,并顯示在貸款詳情屏幕上。后臺數據庫中,我們有一個名為LoanApplicationDetails的表,來存儲貸款軟件的詳情。
該范例應用的用例是通過指定貸款ID來獲取貸款詳情。當貸款已經登記并將抵押物與利息掛鉤后,貸款詳情就可以被獲取。表5顯示了貸款處理應用軟件項目的詳情。
?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數據庫所需的JDBC參數。這些參數存放于一個名為derby.properties的文本文件中,并置于項目的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數據庫提供了兩種JDBC驅動:嵌入式驅動(org.apache.derby.jdbc.EmbeddedDriver)和客戶端/服務器驅動(org.apache.derby.jdbc.ClientDriver)。我在范例應用中使用客戶端/服務器版驅動。
以下是使用ij工具來啟動Derby數據庫服務器并創建新數據庫的命令。
要啟動Derby網絡服務器,需開啟一個命令行窗口,并運行如下命令(請根據你本機的環境改寫DERBY_INSTALL和JAVA_HOME環境變量)。
set DERBY_INSTALL=C:\dev\dbservers\db-derby-10.1.2.1-bin set JAVA_HOME=C:\dev\java\jdk1.6.0 set DERBY_INSTALL=C:\dev\dbservers\db-derby-10.1.3.1-bin set CLASSPATH=%CLASSPATH%;%DERBY_INSTALL%\lib\derby.jar; ??????????????? %DERBY_INSTALL%\lib\derbytools.jar; ??????????????? %DERBY_INSTALL%\lib\derbynet.jar;
cd %DERBY_INSTALL%\frameworks\NetworkServer\bin startNetworkServer.bat
|
要連接數據庫服務器并創建測試數據庫,需開啟另一個命令行窗口并運行以下命令。請確保DERBY_INSTALL和JAVA_HOME環境變量符合你本機的環境。
set JAVA_HOME=C:\dev\java\jdk1.6.0 set DERBY_INSTALL=C:\dev\dbservers\db-derby-10.1.3.1-bin set CLASSPATH=%DERBY_INSTALL%\lib\derbyclient.jar; ??????????????? %DERBY_INSTALL%\lib\derbytools.jar;.
%JAVA_HOME%\bin\java org.apache.derby.tools.ij connect 'jdbc:derby://localhost:1527/LoanDB;create=true';
|
測試
要編譯Java源代碼,classpath需包括derby.jar和junit4.jar文件,這兩個文件在項目的lib目錄下。Classpath還需包括etc、etc/jdbc和etc/log4j目錄,這樣應用程序才能訪問JDBC屬性文件和Log4J配置文件。我創建了一個Ant構建腳本(在JdbcApp/build目錄下)來自動完成編譯和打包Java源代碼的工作。
用于測試貸款詳情數據庫訪問對象的測試類名為LoanAppDetailsDAOTest。我們傳入貸款ID和貸款人參數就可以獲取貸款詳情。
以下部分顯示了JDBC 4.0中自動加載JDBC驅動和基于標注的SQL查詢特性的代碼范例。
JDBC驅動的自動加載
BaseDAO抽象類有一個名為getConnection的方法用以獲得一個數據庫連接。以下代碼片斷顯示了該方法(注意,我們不必注冊JDBC驅動)。只要java.sql.Driver文件中有合適的驅動程序類名(org.apache.derby.jdbc.ClientDriver),JDBC驅動將被自動加載。
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標注
LoanAppDetailsQuery接口有標注了的SQL查詢,用以獲取活躍貸款清單(criteria is loanstatus="A")和某貸款人的貸款詳情(在一個貸款人有多筆貸款的情況下)。在上文中,我們已經了解過了這些SQL標注。這里的范例代碼顯示了我們如何使用標注來調用已定義的SQL查詢。
public DataSet<LoanAppDetails> getAllActiveLoans() throws Exception { ??????? // Get Connection ??????? Connection conn = getConnection(); ??????? LoanAppDetailsQuery query = null; ??????? DataSet<LoanAppDetails> loanDetails = null; ??????? query = QueryObjectFactory.createQueryObject( ??????????????????????? LoanAppDetailsQuery.class, conn); ??????? loanDetails = query.getAllActiveLoans(); ??????? return loanDetails; }
public DataSet<LoanAppDetails> getLoanDetailsByBorrowerName( ??????????????? String borrFirstName, String borrLastName) throws Exception { ??????? // Get Connection ??????? Connection conn = getConnection(); ??????? LoanAppDetailsQuery query = null; ??????? DataSet<LoanAppDetails> loanDetails = null; ??????? query = QueryObjectFactory.createQueryObject( ??????????????????????? LoanAppDetailsQuery.class, conn); ??????? loanDetails = query.getLoanDetailsByBorrowerName( ??????????????????????? borrFirstName,borrLastName); ??????? return loanDetails; }
|
結論
JDBC 4.0在SQL方面為開發者提供了更好的開發體驗。JDBC 4.0的另一個目標是為API增加更豐富的工具以提供企業級JDBC特性管理JDBC資源。并且,JDBC 4.0 API還提供了JDBC驅動的移植方式,使其符合J2EE連接器架構(JCA)規范。這為JDBC廠商提供了向JDBC連接器遷移的能力。在面向企業服務的架構(SPA)中使用JDBC數據源,該移植方式很重要。在SOA中,JDBC數據源可被部署在另一個企業服務總線(ESB)架構內,而不需要為JDBC數據源另寫一份ESB實現代碼。
版權聲明:Techtarget獲Matrix授權發布,如需轉載請聯系Matrix
作者:feichangcai;feichangcai
原文:http://www.matrix.org.cn/resource/article/2006-11-19/Mustang+JDBC_c8c66f03-77c2-11db-bdce-bdc029e475a1.html
凡是有該標志的文章,都是該blog博主Caoer(草兒)原創,凡是索引、收藏
、轉載請注明來處和原文作者。非常感謝。