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