JDBC基礎知識
一、采用JDBC訪(fǎng)問(wèn)數據庫的基本步驟:
A.載入JDBC驅動(dòng)程序
B.定義連接URL
C.建立連接
D.創(chuàng )建Statement對象
E.執行查詢(xún)或更新
F.結果處理
G.關(guān)閉連接
二、載入JDBC驅動(dòng)程序:
1.為了使代碼盡可能地靈活,我們要避免對類(lèi)名的引用進(jìn)行硬編碼(hard-coding),因此我們可以采用從Properties文件中載入驅動(dòng)程序的方法,也可以使用在服務(wù)器中配置數據源(DataSource)的方法來(lái)避免在代碼中硬編碼
2.在開(kāi)發(fā)過(guò)程中要保證CLASSPATH設定中包括驅動(dòng)程序JAR文件所在的路徑。在WEB服務(wù)
器上部署時(shí)要將JAR文件放在Web應用的WEB-INF/lib目錄下。如果多個(gè)Web應用使用相同的數據庫驅動(dòng)程序可以將JAR文件放置在服務(wù)器使用的公共目錄<%CATALINA_HOME%>\common\lib中
三、定義連接URL:
載入JDBC驅動(dòng)程序之后,必須指定數據庫服務(wù)器位置。指向數據庫的URL所使用的協(xié)議是:
jdbc:子協(xié)議,并且載入服務(wù)器的主機名、端口、數據庫名(或引用)。如:Oracle 的連接URL:
jdbc:oracle:thin:@192.168.0.71:1521:UMV2
jdbc:oracle:采用Oracle驅動(dòng)程序
thin:指連接服務(wù)器所采用的模式
@192.168.0.71:服務(wù)器的地址
1521:服務(wù)器的監聽(tīng)端口
UMV2:數據庫名
四、建立連接:
1.一個(gè)數據庫連接(Connection)可以通過(guò)其自身的getMetaData()來(lái)獲取它的自身信息
2.默認情況下一個(gè)數據庫的連接是自動(dòng)提交模式的(auto-commit),也就是說(shuō)每當一個(gè)SQL語(yǔ)句
被執行后其改變結果都會(huì )被自動(dòng)提交,如果auto-commit模式被關(guān)閉,那么方法commit()必須被顯式調用以提交改變結果,否則的話(huà)所有對數據庫操作的結果都不會(huì )被保存
五、創(chuàng )建Statement對象:
在同一時(shí)間下,每個(gè)Statement對象只能打開(kāi)一個(gè)ResultSet對象。所以,假如有兩個(gè)同樣結果的結果集在交叉訪(fǎng)問(wèn),那么這兩個(gè)結果集必定為兩個(gè)不同的Statement對象所創(chuàng )建。如果在打開(kāi)一個(gè)新的結果集的時(shí)候存在一個(gè)已經(jīng)打開(kāi)的結果集,則這個(gè)已經(jīng)存在的結果集會(huì )被隱式的關(guān)閉
六、執行查詢(xún)或更新:
在Statement對象中可以執行如下的操作:
A.查詢(xún)操作:executeQuery(SQL語(yǔ)句) B.維護操作:executeUpdate(SQL語(yǔ)句)
C.批處理操作:executeBath()
七、結果處理:
1.ResultSet中行的第一列索引為1,而非0,訪(fǎng)問(wèn)ResultSet中的數據時(shí)要使用列名,而非索引
但要注意使用列名作為查詢(xún)條件是大小寫(xiě)敏感的。
2.JDBC1.0中,我們只能在ResultSet中向前移動(dòng);在JDBC2.0中,我們可以在ResultSet中向
下(next)或向上(previous)移動(dòng),同樣也可以移到特定的行(relative,absolute)
3.默認情況下ResultSet是不可更新的,且只能向前移動(dòng)。下面的代碼顯示了如何創(chuàng )建一個(gè)可滾動(dòng)的、對更新敏感的ResultSet
Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE2");
// rs will be scrollable, will not show changes made by others,
// and will be updatable
4.ResultSet和ResultSetMetaData沒(méi)有直接提供方法返回查詢(xún)所返回的行數。然而,在JDBC
2.0中,可以通過(guò)調用last()方法將游標定位到ResultSet的最后一行,然后調用getRow()方
法獲取當前的行號。在JDBC1.0中,確定行數的惟一方式是重復調用ResultSet的next()方法,
直到它返回false為至
八、關(guān)閉連接:
在關(guān)閉數據庫連接時(shí)應該以ResultSet、Statement、Connection的順序進(jìn)行
JDBC-PreparedStatement(預備語(yǔ)句)
一、PreparedStatement(預備語(yǔ)句)的創(chuàng )建:
首先按照標準的格式創(chuàng )建參數化語(yǔ)句,在實(shí)際使用之前發(fā)送參數到數據庫進(jìn)行編譯。用問(wèn)號表示語(yǔ)句中應該為具體的值所替換的位置。每次使用預備語(yǔ)句時(shí),只需要使用相應的setXxx調用,替換語(yǔ)句中標記出來(lái)的參數。然后就可以和常規的語(yǔ)句一樣,使用executeQuery或execute/executeUpdate修改表中的數據。例如:
Connection connection = DriverManager.getConnection (url,username,password);
// 創(chuàng )建帶問(wèn)號的參數化語(yǔ)句
String template = " UPDATE music SET price=? WHERE id=? ";
PreparedStatement statement = connection.prepareStatement (template);
float newPrices[] = getNewPrices();
int recordingIDs = getIDs();
for(int i=0; i<recordingIDs.length;i++){
// 用setXxx代替?
statement.setFloat(1,newPrices[i]);
statement.setInt(2,recordingIDs[i]);
// 執行預備語(yǔ)句
statement.execute();}
二、使用PreparedStatement的好處:
1.依賴(lài)于服務(wù)器對預編譯查詢(xún)的支持,以及驅動(dòng)程序處理原始查詢(xún)的效率,預備語(yǔ)句在性能上的優(yōu)勢可能有很大的不同。
2.安全是預備語(yǔ)句的另外一個(gè)特點(diǎn),我們推薦在通過(guò)HTML表單接受用戶(hù)輸入,然后對數據庫進(jìn)行更新時(shí),一定要使用預備語(yǔ)句或存儲過(guò)程。
3.預備語(yǔ)句還能夠正確地處理嵌入在字符串中的引號以及處理非字符數據(比如向數據庫發(fā)送序列化后的對象)
JDBC-CallableStatement(可調用語(yǔ)句)
一、使用CallableStatement(可調用語(yǔ)句)的優(yōu)缺點(diǎn):
1.優(yōu)點(diǎn):語(yǔ)法錯誤可以在編譯時(shí)找出來(lái),而非在運行期間;數據庫存儲過(guò)程的運行可能比常規的
SQL查詢(xún)快得多;程序員只需知道輸入和輸出參數,不需了解表的結構。另外,由于數據庫語(yǔ)言能夠訪(fǎng)問(wèn)數據庫本地的一下兒功能(序列,觸發(fā)器,多重游標),因此用它來(lái)編寫(xiě)存儲過(guò)程可能要比使用Java編程語(yǔ)言要簡(jiǎn)易一些。
2.缺點(diǎn):存儲過(guò)程的商業(yè)邏輯在數據庫服務(wù)器上運行,而非客戶(hù)機或Web服務(wù)器。而行業(yè)的發(fā)展趨勢是盡可能多地將商業(yè)邏輯移出數據庫,將它們放在JavaBean組件(或者在大型的系統中,EnterPrise JavaBean組件)中,在Web構架上采用這種方式的主要動(dòng)機是:數據庫訪(fǎng)問(wèn)和網(wǎng)絡(luò )I/O常常是性能的瓶頸。
二、使用CallableStatement在JAVA中調用數據庫存儲過(guò)程:
1.定義對數據庫過(guò)程的調用
A.無(wú)參數過(guò)程:{ call procedure_name}
B. 僅有輸入參數的過(guò)程:{call procedure_name(?,?...)}
C.有一個(gè)輸出參數的過(guò)程:{? Call procedure_name}
D.既有輸入參數又有輸出參數的過(guò)程{?=call procedure_name(?,?...)}
在過(guò)程的4種形式中要注意過(guò)程可能返回多個(gè)輸出參數,并且參數的索引值從輸出參數開(kāi)始。因此前面最后例子中,第一個(gè)輸入參數的索引值是2而不是1。
2.為過(guò)程準備CallableStatement
String procedure = “{ ? = call procedure_name(?,?) }”;
CallableStatement statement = connection.prepareCall(procedure);
3.提供輸入參數的值
在執行存儲過(guò)程之前,我們需要調用與所要設置的項以及參數的類(lèi)型相對應的setXxx,替換標記出來(lái)的輸入參數
Statement.setString(2,”name”);
4.注冊輸出參數的類(lèi)型
我們必須使用registerOutParameter注冊每個(gè)輸出參數的JDBC類(lèi)型
Statement.registerOutParameter(n,type);
5.執行這個(gè)存儲過(guò)程
Statement.execute();
6.訪(fǎng)問(wèn)返回的輸出參數
可以通過(guò)調用getXxx訪(fǎng)問(wèn)每個(gè)對應的輸出參數
例如:
Connection connection = DriverManager.getConnection(url,username,password);
String procedure = “{ ? = call myProc(?,?)}”;
CallableStatement statement = connection.prepareCall(procedure);
statement.setString(2,×××);
statement.setFloat(3,×××);
statement.registerOutParameter(1,Types.INTEGER);
statement.execute();
int row = statement.getInt(1);
JDBC-Transation(事務(wù)處理)
一、Transation(事務(wù)處理)的概念:
在更新數據庫時(shí),默認情況下,更改是永久性寫(xiě)入到數據庫。然而這種默認行為可以通過(guò)編寫(xiě)程序來(lái)關(guān)閉。在自動(dòng)交付關(guān)閉的情況下,如果在更新時(shí)發(fā)生問(wèn)題,則對數據庫的每個(gè)更改都能夠取消(或者說(shuō)回退到最初的值)。如果更新成功,那么之后可以將這些更改永久性提交給數據庫。這種方式也稱(chēng)為事務(wù)管理。
我們需要確保,要么所有的操作都發(fā)生,要么所有的操作都不發(fā)生。這就是事務(wù)管理的原則。
二、在JAVA中使用Transation(事務(wù)管理)保證數據庫的完整性:
我們使用try-catch-finally塊來(lái)正確地應對事務(wù)管理,首先,記錄自動(dòng)提交的當前狀態(tài)。然后,在try塊中,調用setAutoCommit(false)并執行一系列的查詢(xún)或更新。如果發(fā)生故障,則在catch塊中調用rollback;如果事務(wù)成功,則在try塊的結尾調用commit。不管哪種方式,都在finally塊中重置自動(dòng)提交的狀態(tài)。例如:
Connection connection = DriverManager.getConnection(url,username,password);
boolean autoCommit = connection.getAutoCommit();
Statement statement;
try{
connection.setAutoCommit(false); // 關(guān)閉數據庫的自動(dòng)提交
statement = connection.createStatement();
statement.execute(…);
statement.execute(..);
…
connection.commit(); // 如果所有語(yǔ)句執行成功則提交事務(wù)
}
catch(SQLException sqle){
connection.rollback(); // 如果有異常發(fā)生則回滾所有的事務(wù)
}
finally{
if(statement!=null){statement.close();}
connection.setAutoCommit(autoCommit); // 重置自動(dòng)提交的狀態(tài)
}
上面的代碼中,從DriverManager獲取連接的語(yǔ)句在try/catch塊之外。這樣除非成功獲取連接,否則不會(huì )調用rollback。如果把獲取連接的語(yǔ)句放在try/catch快之內,一旦在連接成功后發(fā)生異常,由于rollback的作用會(huì )把已經(jīng)建立的連接斷開(kāi)。但是getConnection方法也會(huì )拋出SQLException異常這個(gè)異常要么被外圍的方法重新拋出,要么在單獨的try/catch塊內捕獲。
JDBC的常用API
一、Connection接口:
1.createStatement():創(chuàng )建數據庫連接
2.prepareStatement(String sql):創(chuàng )建預處理語(yǔ)句
3.prepareCall(String sql):創(chuàng )建可調用語(yǔ)句
4.getAutoCommit():獲取自動(dòng)提交的模式
5.setAutoCommit():設置自動(dòng)提交的模式
6.commit():提交所執行的SQL語(yǔ)句
7.rollback():回滾所執行的SQL語(yǔ)句
8.getMetaData():獲取一個(gè)DatabaseMetaData對象,該對象包含了有關(guān)數據庫的基本信息
9.close():關(guān)閉數據庫連接
10.isClose():判斷數據庫連接是否超時(shí)或被顯示關(guān)閉
二、Statement接口:
1.execute(String sql):執行SQL語(yǔ)句,如果返回值是結果集則為true,否則為false
2.executeQuery(String sql):執行SQL語(yǔ)句,返回值為ResultSet
3.executeUpdate(String sql):執行SQL語(yǔ)句,返回值為所影響的行數
4.addBatch(String sql):向當前Statement對象的命令列表中添加新的批處理SQL語(yǔ)句
5.clearBatch():清空當前Statement對象的命令列表
6.executeBatch():執行當前Statement對象的批處理語(yǔ)句,返回值為每個(gè)語(yǔ)句所影響的函數數組
7.getConnection():返回創(chuàng )建了該Statement對象的Connection對象
8.getQueryTimeout():獲取等待處理結果的時(shí)間
9.setQueryTimeout():設置等待處理結果的時(shí)間
三、ResultSet接口:
1.first()/beforeFirst():將游標移動(dòng)到ResultSet中第一條記錄(的前面)
2.last()/afterLast():將游標移動(dòng)到ResultSet中最后一條記錄(的后面)
3.absolute(int column):將游標移動(dòng)到相對于第一行的指定行,負數則為相對于最后一條記錄
4.relative(int rows):將游標移動(dòng)到相對于當前行的第幾行,正為向下,負為向上
5.next():將游標下移一行
6.previous():將游標上移一行
7.insertRow():向當前ResultSet和數據庫中被插入行處插入一條記錄
8.deleteRow():將當前ResultSet中的當前行和數據庫中對應的記錄刪除
9.updateRow():用當前ResultSet中已更新的記錄更新數據庫中對應的記錄
10.cancelUpdate():取消當前對ResultSet和數據庫中所做的操作
11.findColumn(String columnName):返回當前ResultSet中與指定列名對應的索引
12.getRow():返回ResultSet中的當前行號
13.refreshRow():更新當前ResultSet中的所有記錄
14.getMetaData():返回描述ResultSet的ResultSetMetaData對象
15.isAfterLast(): 是否到了結尾
16.isBeforeFirst(): 是否到了開(kāi)頭
17.isFirst():是否第一條記錄
18.isLast(): 是否最后一條記錄
19.wasNull():檢查列值是否為NULL值,如果列的類(lèi)型為基本類(lèi)型,且數據庫中的值為0,那么
這項檢查就很重要。由于數據庫NULL也返回0,所以0值和數據庫的NULL不能區分。如果列的類(lèi)型為對象,可以簡(jiǎn)單地將返回值與null比較
20.close():關(guān)閉當前ResultSet
四、ResultSetMetaData接口:
1.getColumnCount():返回ResultSet中列的數目
2.getColumnName():返回列在數據庫中的名稱(chēng)
3.getColumnType():返回列的SQL類(lèi)型
4.isReadOnly():表示該數據項是否為只讀值
5.isNullable():表示該列是否可以存儲NULL
基于JDBC的數據庫連接池技術(shù)研究與應用
Java應用程序訪(fǎng)問(wèn)數據庫的基本原理
在Java語(yǔ)言中,JDBC(Java DataBase Connection)是應用程序與數據庫溝通的橋梁, 即Java語(yǔ)言通過(guò)JDBC技術(shù)訪(fǎng)問(wèn)數據庫。JDBC是一種“開(kāi)放”的方案,它為數據庫應用開(kāi)發(fā)人員、數據庫前臺工具開(kāi)發(fā)人員提供了一種標準的應用程序設計接口,使開(kāi)發(fā)人員可以用純Java語(yǔ)言編寫(xiě)完整的數據庫應用程序。JDBC提供兩種API,分別是面向開(kāi)發(fā)人員的API和面向底層的JDBC驅動(dòng)程序API,底層主要通過(guò)直接的JDBC驅動(dòng)和JDBC-ODBC橋驅動(dòng)實(shí)現與數據庫的連接。
一般來(lái)說(shuō),Java應用程序訪(fǎng)問(wèn)數據庫的過(guò)程(如圖1所示)是:
?、傺b載數據庫驅動(dòng)程序;
?、谕ㄟ^(guò)JDBC建立數據庫連接;
?、墼L(fǎng)問(wèn)數據庫,執行SQL語(yǔ)句;
?、軘嚅_(kāi)數據庫連接。
JDBC作為一種數據庫訪(fǎng)問(wèn)技術(shù),具有簡(jiǎn)單易用的優(yōu)點(diǎn)。但使用這種模式進(jìn)行Web應用程序開(kāi)發(fā),存在很多問(wèn)題:首先,每一次Web請求都要建立一次數據庫連接。建立連接是一個(gè)費時(shí)的活動(dòng),每次都得花費0.05s~1s的時(shí)間,而且系統還要分配內存資源。這個(gè)時(shí)間對于一次或幾次數據庫操作,或許感覺(jué)不出系統有多大的開(kāi)銷(xiāo)??墒菍τ诂F在的Web應用,尤其是大型電子商務(wù)網(wǎng)站,同時(shí)有幾百人甚至幾千人在線(xiàn)是很正常的事。在這種情況下,頻繁的進(jìn)行數據庫連接操作勢必占用很多的系統資源,網(wǎng)站的響應速度必定下降,嚴重的甚至會(huì )造成服務(wù)器的崩潰。不是危言聳聽(tīng),這就是制約某些電子商務(wù)網(wǎng)站發(fā)展的技術(shù)瓶頸問(wèn)題。其次,對于每一次數據庫連接,使用完后都得斷開(kāi)。否則,如果程序出現異常而未能關(guān)閉,將會(huì )導致數據庫系統中的內存泄漏,最終將不得不重啟數據庫。還有,這種開(kāi)發(fā)不能控制被創(chuàng )建的連接對象數,系統資源會(huì )被毫無(wú)顧及的分配出去,如連接過(guò)多,也可能導致內存泄漏,服務(wù)器崩潰。
數據庫連接池(connection pool)的工作原理
1、基本概念及原理
數據庫連接池的基本思想就是為數據庫連接建立一個(gè)“緩沖池”。預先在緩沖池中放入一定數量的連接,當需要建立數據庫連接時(shí),只需從“緩沖池”中取出一個(gè),使用完畢之后再放回去。我們可以通過(guò)設定連接池最大連接數來(lái)防止系統無(wú)盡的與數據庫連接。更為重要的是我們可以通過(guò)連接池的管理機制監視數據庫的連接的數量、使用情況,為系統開(kāi)發(fā)、測試及性能調整提供依據。
2、服務(wù)器自帶的連接池
JDBC的API中沒(méi)有提供連接池的方法。一些大型的WEB應用服務(wù)器如BEA的WebLogic和IBM的WebSphere等提供了連接池的機制,但是必須有其第三方的專(zhuān)用類(lèi)方法支持連接池的用法。
連接池關(guān)鍵問(wèn)題分析
1、并發(fā)問(wèn)題
為了使連接管理服務(wù)具有最大的通用性,必須考慮多線(xiàn)程環(huán)境,即并發(fā)問(wèn)題。這個(gè)問(wèn)題相對比較好解決,因為Java語(yǔ)言自身提供了對并發(fā)管理的支持,使用synchronized關(guān)鍵字即可確保線(xiàn)程是同步的。使用方法為直接在類(lèi)方法前面加上synchronized關(guān)鍵字,如:
public synchronized Connection getConnection()
2、多數據庫服務(wù)器和多用戶(hù)
對于大型的企業(yè)級應用,常常需要同時(shí)連接不同的數據庫(如連接Oracle和Sybase)。如何連接不同的數據庫呢?我們采用的策略是:設計一個(gè)符合單例模式的連接池管理類(lèi),在連接池管理類(lèi)的唯一實(shí)例被創(chuàng )建時(shí)讀取一個(gè)資源文件,其中資源文件中存放著(zhù)多個(gè)數據庫的url地址(<poolName.url>)、用戶(hù)名(<poolName.user>)、密碼(<poolName.password>)等信息。如tx.url=172.21.15.123:5000/tx_it,tx.user=yang,tx.password=yang321。根據資源文件提供的信息,創(chuàng )建多個(gè)連接池類(lèi)的實(shí)例,每一個(gè)實(shí)例都是一個(gè)特定數據庫的連接池。連接池管理類(lèi)實(shí)例為每個(gè)連接池實(shí)例取一個(gè)名字,通過(guò)不同的名字來(lái)管理不同的連接池。
對于同一個(gè)數據庫有多個(gè)用戶(hù)使用不同的名稱(chēng)和密碼訪(fǎng)問(wèn)的情況,也可以通過(guò)資源文件處理,即在資源文件中設置多個(gè)具有相同url地址,但具有不同用戶(hù)名和密碼的數據庫連接信息。
3、事務(wù)處理
我們知道,事務(wù)具有原子性,此時(shí)要求對數據庫的操作符合“ALL-ALL-NOTHING”原則,即對于一組SQL語(yǔ)句要么全做,要么全不做。
在Java語(yǔ)言中,Connection類(lèi)本身提供了對事務(wù)的支持,可以通過(guò)設置Connection的AutoCommit屬性為false,然后顯式的調用commit或rollback方法來(lái)實(shí)現。但要高效的進(jìn)行Connection復用,就必須提供相應的事務(wù)支持機制??刹捎妹恳粋€(gè)事務(wù)獨占一個(gè)連接來(lái)實(shí)現,這種方法可以大大降低事務(wù)管理的復雜性。
4、連接池的分配與釋放
連接池的分配與釋放,對系統的性能有很大的影響。合理的分配與釋放,可以提高連接的復用度,從而降低建立新連接的開(kāi)銷(xiāo),同時(shí)還可以加快用戶(hù)的訪(fǎng)問(wèn)速度。
對于連接的管理可使用空閑池。即把已經(jīng)創(chuàng )建但尚未分配出去的連接按創(chuàng )建時(shí)間存放到一個(gè)空閑池中。每當用戶(hù)請求一個(gè)連接時(shí),系統首先檢查空閑池內有沒(méi)有空閑連接。如果有就把建立時(shí)間最長(cháng)(通過(guò)容器的順序存放實(shí)現)的那個(gè)連接分配給他(實(shí)際是先做連接是否有效的判斷,如果可用就分配給用戶(hù),如不可用就把這個(gè)連接從空閑池刪掉,重新檢測空閑池是否還有連接);如果沒(méi)有則檢查當前所開(kāi)連接池是否達到連接池所允許的最大連接數(maxConn),如果沒(méi)有達到,就新建一個(gè)連接,如果已經(jīng)達到,就等待一定的時(shí)間(timeout)。如果在等待的時(shí)間內有連接被釋放出來(lái)就可以把這個(gè)連接分配給等待的用戶(hù),如果等待時(shí)間超過(guò)預定時(shí)間timeout,則返回空值(null)。系統對已經(jīng)分配出去正在使用的連接只做計數,當使用完后再返還給空閑池。對于空閑連接的狀態(tài),可開(kāi)辟專(zhuān)門(mén)的線(xiàn)程定時(shí)檢測,這樣會(huì )花費一定的系統開(kāi)銷(xiāo),但可以保證較快的響應速度。也可采取不開(kāi)辟專(zhuān)門(mén)線(xiàn)程,只是在分配前檢測的方法。
5、連接池的配置與維護
連接池中到底應該放置多少連接,才能使系統的性能最佳?系統可采取設置最小連接數(minConn)和最大連接數(maxConn)來(lái)控制連接池中的連接。最小連接數是系統啟動(dòng)時(shí)連接池所創(chuàng )建的連接數。如果創(chuàng )建過(guò)多,則系統啟動(dòng)就慢,但創(chuàng )建后系統的響應速度會(huì )很快;如果創(chuàng )建過(guò)少,則系統啟動(dòng)的很快,響應起來(lái)卻慢。
可以在開(kāi)發(fā)時(shí),設置較小的最小連接數,開(kāi)發(fā)起來(lái)會(huì )快,而在系統實(shí)際使用時(shí)設置較大的,因為這樣對訪(fǎng)問(wèn)客戶(hù)來(lái)說(shuō)速度會(huì )快些。最大連接數是連接池中允許連接的最大數目,具體設置多少,要看系統的訪(fǎng)問(wèn)量,可通過(guò)反復測試,找到最佳點(diǎn)。
如何確保連接池中的最小連接數呢?有動(dòng)態(tài)和靜態(tài)兩種策略。動(dòng)態(tài)即每隔一定時(shí)間就對連接池進(jìn)行檢測,如果發(fā)現連接數量小于最小連接數,則補充相應數量的新連接,以保證連接池的正常運轉。靜態(tài)是發(fā)現空閑連接不夠時(shí)再去檢查。
連接池的實(shí)現
1、連接池模型
連接池類(lèi)是對某一數據庫所有連接的“緩沖池”,主要實(shí)現以下功能:①從連接池獲取或創(chuàng )建可用連接;②使用完畢之后,把連接返還給連接池;③在系統關(guān)閉前,斷開(kāi)所有連接并釋放連接占用的系統資源;④還能夠處理無(wú)效連接(原來(lái)登記為可用的連接,由于某種原因不再可用,如超時(shí),通訊問(wèn)題),并能夠限制連接池中的連接總數不低于某個(gè)預定值和不超過(guò)某個(gè)預定值。
連接池管理類(lèi)是連接池類(lèi)的外覆類(lèi)(wrapper),符合單例模式,即系統中只能有一個(gè)連接池管理類(lèi)的實(shí)例。其主要用于對多個(gè)連接池對象的管理,具有以下功能:①裝載并注冊特定數據庫的JDBC驅動(dòng)程序;②根據屬性文件給定的信息,創(chuàng )建連接池對象;③為方便管理多個(gè)連接池對象,為每一個(gè)連接池對象取一個(gè)名字,實(shí)現連接池名字與其實(shí)例之間的映射;④跟蹤客戶(hù)使用連接情況,以便需要是關(guān)閉連接釋放資源。連接池管理類(lèi)的引入主要是為了方便對多個(gè)連接池的使用和管理,如系統需要連接不同的數據庫,或連接相同的數據庫但由于安全性問(wèn)題,需要不同的用戶(hù)使用不同的名稱(chēng)和密碼。
2、連接池實(shí)現
下面給出連接池類(lèi)和連接池管理類(lèi)的主要屬性及所要實(shí)現的基本接口:
public class DBConnectionPool implements TimerListener{
private int checkedOut;//已被分配出去的連接數
private ArrayList freeConnections = new ArrayList();//容器,空閑池,根據創(chuàng )建時(shí)間順序存放已創(chuàng )建
尚未分配出去的連接
private int minConn;//連接池里連接的最小數量
private int maxConn;//連接池里允許存在的最大連接數
private String name;//為這個(gè)連接池取個(gè)名字,方便管理
private String password;//連接數據庫時(shí)需要的密碼
private String url;//所要創(chuàng )建連接的數據庫的地址
private String user;//連接數據庫時(shí)需要的用戶(hù)名
public Timer timer;//定時(shí)器
public DBConnectionPool(String name, String URL, String user, Stringpassword, int maxConn)
public synchronized void freeConnection(Connection con) //使用完畢之后把連接返還給空閑池
public synchronized Connection getConnection(long timeout)//得到一個(gè)連接,timeout是等待時(shí)間
public synchronized void release()//斷開(kāi)所有連接,釋放占用的系統資源
private Connection newConnection()//新建一個(gè)數據庫連接
public synchronized void TimerEvent() //定時(shí)器事件處理函數
}
public class DBConnectionManager {
static private DBConnectionManager instance;//連接池管理類(lèi)的唯一實(shí)例
static private int clients;//客戶(hù)數量
private ArrayList drivers = new ArrayList();//容器,存放數據庫驅動(dòng)程序
private HashMap pools = new HashMap ();//以name/value的形式存取連接池對象的名字及連接池對象
private void loadDrivers(Properties props)//裝載數據庫驅動(dòng)程序
private void createPools(Properties props)//根據屬性文件提供的信息,創(chuàng )建一個(gè)或多個(gè)連接池
private DBConnectionManager()//私有構造函數,在其中調用初始化函數init()
private void init()//初始化連接池管理類(lèi)的唯一實(shí)例,由私有構造函數調用
static synchronized public DBConnectionManager getInstance()//如果唯一的實(shí)例instance已經(jīng)創(chuàng )建,直接返回這個(gè)實(shí)例;否則,調用私有構造函數,創(chuàng )建連接池管理類(lèi)的唯一實(shí)例
public Connection getConnection(String name)//從名字為name的連接池對象//中得到一個(gè)連接
public Connection getConnection(String name, long time)//從名字為name 的連接池對象中取得一個(gè)連接,time是等待時(shí)間
public void freeConnection(String name, Connection con)//釋放一個(gè)連接name是一個(gè)連接池對象的名
public synchronized void release()//釋放所有資源
}
3、連接池使用
上面所實(shí)現的連接池在程序開(kāi)發(fā)時(shí)如何應用到系統中呢?下面以Servlet為例說(shuō)明連接池的使用。
Servlet的生命周期是:在開(kāi)始建立servlet時(shí),調用其初始化(init)方法。之后每個(gè)用戶(hù)請求都導致一個(gè)調用前面建立的實(shí)例的service方法的線(xiàn)程。最后,當服務(wù)器決定卸載一個(gè)servlet時(shí),它首先調用該servlet的 destroy方法。 根據servlet的特點(diǎn),我們可以在初始化函數中生成連接池管理類(lèi)的唯一實(shí)例(其中包括創(chuàng )建一個(gè)或多個(gè)連接池)。如:
public void init() throws ServletException
{
// getInstance()?DBConnectionManager()?init()
connMgr = DBConnectionManager.getInstance();
}
然后就可以在service方法中通過(guò)連接池名稱(chēng)使用連接池,執行數據庫操作。最后在destroy方法中釋放占用的系統資源,如:
public void destroy() {
connMgr.release();
super.destroy();
}
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請
點(diǎn)擊舉報。