優(yōu)化JDBC編程-多提提意見
發(fā)表時間:2023-08-17 來源:明輝站整理相關軟件相關文章人氣:
[摘要]優(yōu)化JDBC編程這是我根據MS SQL SERVER 2000 JDBC DRIVER HELP,并參考其它資料整理而成。ms的這個幫助文件實在有失大家風范,示例代碼很.....有興趣者可以去下載h...
優(yōu)化JDBC編程
這是我根據MS SQL SERVER 2000 JDBC DRIVER HELP,并參考其它資料整理而成。ms的這個幫助文件實在有失大家風范,示例代碼很.....有興趣者可以去下載http://download.microsoft.com/download/SQLSVR2000/jdbc/2000/NT45XP/EN-US/setup.exe。由于本人水平有限,文中不當之處請大家批評指正。
1.盡量減少對數據庫元數據方法的使用
同樣是產生一個ResultSet對象,DatabaseMetaData 對象的方法比其它JDBC方法相對要慢,因此平繁使用該方法會降低系統(tǒng)的性能。在程序中應當對產生的結果集信息進行高速緩存,比如將getTypeInfo()返回的結果集存入Vector或Hashtable中,這樣可大大提高程序的效率。
2.應避免的方法調用模式
在方法調用時應當盡量避免傳入null做為參數,雖然有時能執(zhí)行成功,但這對DB Server負擔很重。其實在很多情況下所需的參數是已知的。比如:
//這里略去了捕獲違例代碼(下同)。
DatabaseMetaData md=...;
ResultSet rs=md.getTables(null,null,"authors",null);//取得MS SQL SERVER pubs數據庫中authors表的信息.
應當寫成:
ResultSet rs=md.getTables("northwind","dbo","authors",new String[]{"TABLE"});
這樣使程序更有效可靠。
3.使用啞查詢語句來取得表的相關特征信息
一個啞查詢語句(Dummy Query,譯為啞查詢不知是否恰當,愿與大家探討)不會產生有記錄的結果集,比如:select * from tableName where 1=0,因為條件永不成立,DB Server 不會執(zhí)行這條語句。因此,在不需產生記錄行的情況下,啞查詢能極大地提高程序的執(zhí)行效率。比如我們要了解一個表的有關列信息時,上面的語句比select * from tableName這個語句要高效得多,后者數據庫服務器要檢索所有的行并返回一個記錄集,而前者不需要。針對這一問題,JDBC可以有以下兩種方法:
case 1:使用getColumns()方法
//getColumns()是DatabaseMetaData的一個方法,其有關信息請查閱JDK1.3文檔
ResultSet rs=md.getColumns("pubs","dbo","authors",...);//返回一個有記錄的結果集
while(rs.next())//通過滾動結果集取得列名
System.out.println(rs.getString(4));
case 2:使用getMetaData()方法
Statement stmt=conn.createStatement();
//數據庫服務器永遠不會執(zhí)行這條查詢語句
ResultSet rs=stmt.executeQuery("select * from authors where 1=0");
ResultSetMetaData rsmd=rs.getMetaData();
int colCount=rsmd.getColumnCount();//取得列數
for(int col=1;col<=colCount;col++)
System.out.println(rsmd.getColumnName(col));
//!這里列的順序是select后列出現的順序,并不一定與表中列順序對應
通過以上的分析,第二種方法應是我們的選擇。
4.關于存儲過程的調用
由于所有的JDBC驅動總是將SQL語句作為字符串發(fā)送到數據庫服務器,數據庫服務器經過語法分析、參數類型驗證,然后將參數轉換成正確的數據類型再去執(zhí)行。比如有這么一個存儲過程:
CallableStatement cstmt=conn.prepareCall("{call getCustomerName(123)}");
//獲得指定id的客戶的名字,輸入參數,id是個正整數
ResultSet rs=cstmt.executeQuery();
在這里我們認為123是一個正整數,但實際"call getCustomerName(123)"作為字符串整個被發(fā)送到數據庫服務器端,數據庫服務器經過分析,離析出"123"將其轉換為整數型值再做為參數送給存儲過程執(zhí)行。很明顯,這樣效率極低,因為我們把已知的東西仍要服務器去判斷,這無疑額外加重了服務器的負擔。做為優(yōu)化也是我們常見的存儲過程的調用方法應是:
CallableStatement cstmt=conn.prepareCall("call getCustomerName(?)");
cstmt.setLong(1,123);//將值和類型信息編碼后發(fā)送
ResultSet rs=cstmt.executeQuery();
//do something
5.正確使用Statement和PreparedStatement對象及其execute方法
Statement 對象是為僅執(zhí)行一次的查詢語句優(yōu)化而設計的,PreparedStatement 對象是為兩次或更多次執(zhí)行同一查詢語句而設計的。PreparedStatement 對象第一次執(zhí)行一個準備好的查詢要花一定的代價,然而它帶來的好處是為以后的查詢加快了速度;因為SQL語句已經進行編譯并放入高速緩存,你可以一直重復使用;想要改變查詢條件獲得不同的結果集只需用setXXX方法改變主機變量(?)的值就行了。
由于PreparedStatement 及 CallableStatement都是Statement的子類,所以它們都有execute(String sql),executeQuery(String sql),executeUpdate(String sql),executeBatch()方法。
execute(String sql)方法返回一個boolean值,它執(zhí)行任意復雜的sql語句,可以產生多個結果集。如果有結果產生返回 true,如果沒有結果集產生或僅是一個更新記數則返回 false。它產生的結果集可以通過getResultSet()和getMoreResults()獲得,更新記數可通過getUpdateCount()獲得。顯然execute(String sql)方法的使用要復雜一些,因此如果只是簡單的查詢或更新操作請使用executeQuery(String sql)和executeUpdate(String sql)方法。executeUpdate(String sql)能執(zhí)行INSERT,UPDATE,DELETE語句,及DDL和DML命令(此時返回值為0)。
如果需要進行更多的更新操作,只需將這些更新命令打包后一起提交給數據庫,數據庫一次處理所有的請求,這比逐條提交要高效得多。例如:
//保存當前提交模式
boolean commitState=conn.getAutoCommit();
// 關閉自動提交模式
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
//逐條加入
stmt.addBatch("INSERT INTO employees VALUES (1000, 'Joe Jones')");
stmt.addBatch("INSERT INTO departments VALUES (260, 'Shoe')");
stmt.addBatch("INSERT INTO emp_dept VALUES (1000, 260)");
// 一次提交
int[] updateCounts = stmt.executeBatch();
conn.commit();//使更新生效
conn.setAutoCommit(commitState);//恢復原來的提交模式
PreparedStatement和CallalbeStatement對象的使用基本與Statement一樣,請參閱JDBC2.1API。
6.正確的使用游標
JDBC2.1核心API提供三種結果集類型:forward-only, scroll-insensitive和scroll-sensitive。
forward-only:僅向前型。如果你僅需要前向順序滾動結果集中的所有行,僅向前游標能提供極高的性能;然而它不能從第一行上直接滾動到最后一行,也不能從最后一行滾到第一行。
scroll-insensitive:滾動不敏感型。對于要求較高處理級別的應用來說,滾動不敏感型結果集是一個理想的選擇,它支持向前和向后的記錄集滾動。滾動不敏感型結果集的第一次請求是從數據庫服務端取得所有滿足條件的行,然后將它存儲在客戶端,也就是說是一個包含數據的客戶端靜態(tài)視圖;雖然以后的操作比較快,但數據庫服務器處理第一次的請求非常慢,尤其是當返回的數據量比較大時。因此,如果返回的只是一行記錄我們就不應使用這種游標,使用僅向前就滿足要求了;相反,如果返回的記錄非常多,也不推薦使用這種游標,因為這些數據都存放在內存里,大量的數據將很快使內存耗盡。有些滾動不敏感游標的實現是將數據緩存到數據庫服務器的一個臨時表中,以免占用過多的內存資源。
scroll-sensitive:滾動敏感型,有時也叫鍵集驅動游標。它是在你的數據庫上對滿足條件的記錄行做了一個標識,好像行的主鍵,當你滾動結果集的時候,只有有標識的數據才會返回。由于每次的請求都要產生一次網絡連接,因此速度是很慢的。
7.只返回需要的行或列
聽了上周六范生對Oracle核心的剖析,我算是搞清楚了對表的查詢或更新,數據庫低層操作其實是對磁盤文件的read or write,而I/O操作數據量越大耗時越多,軟盤的讀寫速度大家是有目共睹的。為了避免不必要的數據傳輸,請小心使用select * from ...這樣的語句,如果只需要一列就沒必要返回所有的列,特別是當你不需要的列中含有大數據類型(如BINARY,BLOB,CLOB)或者說數據量較大時,會影響系統(tǒng)性能。
8.使用連接池
連接池對數據庫訪問性能的提高是非常顯著地,因為創(chuàng)建和銷毀一個連接的代價都非常昂貴。連接池實現了數據庫連接的共享,一個連接對象可以被多個用戶多次重復使用。由容器管理的連接池就像是一個租賃公司,誰要使用就租給他一個,用完后還給我,下次要用接著出租,這樣就免去了每次請求都要造個新的,而用完后又把它扔了。
關于連接池技術較為復雜,不過你也完全可以寫自己的連接池對象,如果你看了《JAVA2高級編程》。
[參考資料]
1.MS SQL SERVER 2000 JDBC DRIVER HELP
2.《JAVA2高級編程》
3.《JAVA2核心技術II》
4.《J2EE構建企業(yè)系統(tǒng)專家級解決方案》
5.《JSP高級編程》
6.SUN JDBC2.1 API