一: 選擇合適的JDBC 驅動程序模式,並作爲程序設計的考慮因素之一
到目前爲止,JDBC驅動程序有4種模式。選擇何種模式主要取決於程序的應用範圍。正確選擇合適的模式,使之符合於數據庫程序的設計,是提高程序性能必須考慮的一個方面。這裏,我們給出JDBC驅動程序的4種模式的簡要說明:
模式
|
層
|
工作機制
|
說 明
|
1
|
2
|
JDBC-ODBC 橋
|
把JDBC操作翻譯成對應的ODBC調用。
|
2
|
2
|
本機API/集團式Java驅動
|
把JDBC操作翻譯成針對特定數據庫的調用。
|
3
|
3
|
網絡協議/全Java驅動
|
把JDBC操作翻譯成網絡協議並轉發給中間層服務器。
|
4
|
2
|
本級協議/全Java驅動
|
把JDBC操作直接轉換成不使用ODBC或本機API的本機協議。
|
模式
|
優點
|
缺點
|
1
|
因爲多數RDBMS平臺都支持ODBC 驅動程序,所以使用Jdbc-Odbc 橋能與大量ODBC驅動程序協同工作。
|
1:用戶受底層ODBC驅動程序的功能限制。
|
2:ODBC需要在每個客戶端得到配置。
|
||
3:不能用於applet中,因爲applet不能加載本地調用。
|
||
2
|
不需要轉換成Odbc調用,比模式1的性能要好得多。
|
使用Java本地接口,平臺移植性不好。
|
3
|
廣泛適用於Internet/Intranet的開發,安全性和性能都十分顯著。
|
進行數據庫操作時,需要花費較長的時間。
|
4
|
由於是通過本機協議訪問數據庫,所以性能很高。
|
用戶需要給不同的數據庫使用不同的JDBC驅動程序。
|
說明2:對於進行大型企業開發部署的數據庫開發人員來說採用模式1作爲設計基礎,那簡直就是噩夢。在企業應用中選擇模式3纔是明智的。由於筆者的經驗有限,這裏暫時不對基於模式3的情況進行分析。
小結:關於不同模式驅動程序的選擇,其利弊以及和程序性能三者之間的聯繫,我們這裏就不多說了。雖然模式1有諸多不好,但在後面的分析中我們仍然以模式1作爲我們的實驗環境。
|
Id
|
Name
|
Department
|
Position
|
Address
|
1
|
張三
|
物理系
|
團員
|
昆明
|
方法
|
方法說明
|
使用環境
|
1
|
爲每個用戶建立一個單獨的連接,並反覆使用該連接創建語句,執行相關的數據庫操作。
|
每個用戶必須經過驗證才能使用該連接。
|
2
|
建立連接池
|
多用戶
|
import java.sql.DriverManager;
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver").newInstance();
}
ex.printStackTrace();
}
MakeConnection makeConnect = new MakeConnection();
makeConnect.setName(""+i+"");
makeConnect.start();
}
}
public static void main(String args[]){
TestJdbc test = new TestJdbc();
test.connect();
}
class MakeConnection extends Thread {
public void run(){
//關閉連接的方式,可以是上述4種方式之一
con = DriverManager.getConnection("jdbc:odbc:test");
con.close();
con = null;
}
e.printStackTrace();
}
}
|
方式1
|
方式2
|
方式3
|
方式4
|
預期創建連接數目(個)
|
100
|
100
|
100
|
100
|
創建及關閉連接耗時(毫秒)
|
6,540
|
7,850
|
20,320
|
13,950
|
實際生成連接實例數(個)
|
100
|
100
|
100
|
100
|
剩餘連接實例數 (個)
|
26
|
24
|
24
|
23
|
創建連接消耗內存量(字節)
|
8,800
|
8,800
|
8,800
|
8,718
|
剩餘連接佔用內存量(字節)
|
2,288
|
2,112
|
2,112
|
2,024
|
|
方式1
|
方式2
|
方式3
|
方式4
|
預期創建連接數目(個)
|
100
|
100
|
100
|
100
|
創建及關閉連接耗時(毫秒)
|
6,650
|
6,430
|
9,280
|
9,610
|
實際生成連接實例數(個)
|
63
|
65
|
100
|
100
|
剩餘連接實例數 (個)
|
63
|
65
|
100
|
100
|
創建連接消耗內存量(字節)
|
5,544
|
5,720
|
8,800
|
8,800
|
剩餘連接佔用內存量(字節)
|
5,544
|
5,720
|
8,800
|
8,800
|
接口類型
|
Insert語句
|
Update語句
|
Select語句
|
|||
第一次測試耗時
|
第二次測試耗時
|
第一次測試耗時
|
第二次測試耗時
|
第一次測試耗時
|
第二次測試耗時
|
|
Statement
|
2360 ms
|
2190 ms
|
3790 ms
|
3460 ms
|
3570 ms
|
2530 ms
|
PreparedStatement
|
1270 ms
|
1040 ms
|
2600 ms
|
2410 ms
|
440 ms
|
380 ms
|
方式
|
Select語句
|
|||
執行100次語句結構相同的查詢耗時
|
執行1000次語句結構相同的查詢耗時
|
|||
第一次測試
|
第二次測試
|
第一次測試
|
第二次測試
|
|
方式1
|
1100 ms
|
330 ms
|
3510 ms
|
3020 ms
|
方式2
|
110 ms
|
50 ms
|
440 ms
|
380 ms
|
當程序開發人員爲提高程序性能而絞盡腦汁時,我們首先得想一想在程序中是否使用了緩衝技術。在大量的基於C/S模式的應用程序中,我們可以從兩個不同的角度使用它。從客戶端的角度,我們可以把數據緩衝到本地機,從而避免進行相同的網絡操作;從服務器端的角度,我們也可以緩衝那些經常使用的數據,從而縮短服務器對相同請求的響應時間。合理地使用緩衝技術能有效的提高程序性能。
經常出現這樣的情況,在Java編程中一個或多個對象實例在程序運行期間被反覆的使用。更具體的說,在JDBC編程中,用戶經常使用某幾條查詢語句進行查詢,每次執行查詢都返回相同的結果集,這個時候我們就得考慮:那些反覆使用的結果集是否應該被緩衝起來?如何使用這些結果集?用戶更新數據時,如何保證這些結果集也能隨之更新?
這裏,我們將具體的討論JDBC編程中如何實現對結果集的緩衝。首先,我們要澄清如下幾個概念。
(1):在Java編程中如果要使用類,就得把類實例化成對象。類實例化成對象後,就要分配一塊相應的內存空間存放該實例。通過對象引用,可以對這塊內存空間進行操作。
(2):在JDBC中,執行查詢操作返回的結果集,其實是一個指向數據庫的連接,通過遊標操作可以把需要的數據從數據庫調入Java堆。我們用圖表進一步說明:
(3):對結果集的緩衝實際上是對Java堆中指定的結果集對象進行緩衝,更確切的說應該是緩衝該對象的引用。
基於上述原理,我們決定使用java util 包中的Hashtable實現對結果集對象的緩衝。我們用查詢語句本身作爲該結果集對象的標誌,也就是Hashtable的key字段, value字段則存放了一個Vector對象。此Vector存放了ResultSet的引用“指針”(暫且稱爲指針);Statement或者PreparedStatement的引用“指針”;結果集的記錄數;如果該結果集具有可滾動的特性(JDBC2.0 API的新特性),則還可以存放當前遊標的位置。存放的內容可以根據實際情況調整,但是ResultSet的引用“指針”和 Statement的引用“指針”是必須保存的。具體結構如下圖所示:
但是,我們發現當內存空間很大,並且需要緩存的對象不是太多時,使用Hashtable的確是最省事的辦法。可是,隨着需緩存的對象數目的不斷增加,Hashtable佔用的內存空間也越來越大。爲了,減少對內存的消耗,同時又能夠實現緩存對象的目的。我們決定設置一個上限,當緩存對象的數目超過該上限時則把最不常用的ResultSet替換出來。下圖是強化了的緩存結構:
(注:該數據結構以及LRU算法在jbuilder6.0的Database Pilot代碼包裏有一定介紹。)
當然爲了降低系統消耗,使用的替換算法不能太複雜。另外,可以使用ArrayList和Hashmap來替代Vector和Hashtable,這樣還能提高一點效率。但是ArrayList和Hashmap都是線程不安全的,而Vector和Hashtable卻剛好相反。當我們建立的緩存需要支持多線程訪問時,使用ArrayList和Hashmap就顯得難以控制。所以這裏我們犧牲效率以提高程序的穩定性是有意義的。
最後還得說明一點,當從緩存中刪除一個對象實例時。應按如下步驟進行:
1. 使用close()方法關閉ResultSet(結果集)。
2. 使用close()方法關閉創建該結果集的語句(Statement,PreparedStatement)。
3. 刪除Hasttable中的相應key及其value。
4. 從Vector中刪除key.