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