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