數(shù)據(jù)庫(kù)連接池的基本原理
數(shù)據(jù)庫(kù)連接池負(fù)責(zé)分配、管理和釋放數(shù)據(jù)庫(kù)連接,它允許應(yīng)用程序重復(fù)使用一個(gè)現(xiàn)有的數(shù)據(jù)庫(kù)連接,而再不是重新建立一個(gè);釋放空閑時(shí)間超過(guò)***空閑時(shí)間的數(shù)據(jù)庫(kù)連接來(lái)避免因?yàn)闆](méi)有釋放數(shù)據(jù)庫(kù)連接而引起的數(shù)據(jù)庫(kù)連接遺漏。傳統(tǒng)的數(shù)據(jù)庫(kù)連接方式(指通過(guò)DriverManager和基本實(shí)現(xiàn)DataSource進(jìn)行連接)中,一個(gè)數(shù)據(jù)庫(kù)連接對(duì)象均對(duì)應(yīng)一個(gè)物理數(shù)據(jù)庫(kù)連接,數(shù)據(jù)庫(kù)連接的建立以及關(guān)閉對(duì)系統(tǒng)而言是耗費(fèi)系統(tǒng)資源的操作,在多層結(jié)構(gòu)的應(yīng)用程序環(huán)境中這種耗費(fèi)資源的動(dòng)作對(duì)系統(tǒng)的性能影響尤為明顯。
在多層結(jié)構(gòu)的應(yīng)用程序中通過(guò)連接池(connection pooling)技術(shù)可以使系統(tǒng)的性能明顯得到提到,連接池意味著當(dāng)應(yīng)用程序需要調(diào)用一個(gè)數(shù)據(jù)庫(kù)連接的時(shí),數(shù)據(jù)庫(kù)相關(guān)的接口通過(guò)返回一個(gè)通過(guò)重用數(shù)據(jù)庫(kù)連接來(lái)代替重新創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)連接。通過(guò)這種方式,應(yīng)用程序可以減少對(duì)數(shù)據(jù)庫(kù)連接操作,尤其在多層環(huán)境中多個(gè)客戶端可以通過(guò)共享少量的物理數(shù)據(jù)庫(kù)連接來(lái)滿足系統(tǒng)需求。通過(guò)連接池技術(shù)Java應(yīng)用程序不僅可以提高系統(tǒng)性能同時(shí)也為系統(tǒng)提高了可測(cè)量性。
數(shù)據(jù)庫(kù)連接池是運(yùn)行在后臺(tái)的而且應(yīng)用程序的編碼沒(méi)有任何的影響。此中狀況存在的前提是應(yīng)用程序必須通過(guò)DataSource對(duì)象(一個(gè)實(shí)現(xiàn)javax.sql.DataSource接口的實(shí)例)的方式代替原有通過(guò)DriverManager類(lèi)來(lái)獲得數(shù)據(jù)庫(kù)連接的方式。一個(gè)實(shí)現(xiàn)javax.sql.DataSource接口的類(lèi)可以支持也可以不支持?jǐn)?shù)據(jù)庫(kù)連接池,但是兩者獲得數(shù)據(jù)庫(kù)連接的代碼基本是相同的。
代碼如下:
一個(gè)DataSource對(duì)象通常注冊(cè)在JNDI命名服務(wù)上,應(yīng)用程序可以通過(guò)標(biāo)準(zhǔn)的方式獲得到注冊(cè)在JNDI服務(wù)上的DataSource對(duì)象。
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("jdbc/openbase");
如果當(dāng)前DataSource不支持?jǐn)?shù)據(jù)庫(kù)連接池,應(yīng)用程序?qū)@得一個(gè)和物理數(shù)據(jù)庫(kù)連接對(duì)應(yīng)的Connection對(duì)象。而如果當(dāng)前的DataSource對(duì)象支持?jǐn)?shù)據(jù)庫(kù)連接池,應(yīng)用程序自動(dòng)獲得重用的數(shù)據(jù)庫(kù)連接而不用創(chuàng)建新的數(shù)據(jù)庫(kù)連接。重用的數(shù)據(jù)庫(kù)連接和新建立連接的數(shù)據(jù)庫(kù)連接使用上沒(méi)有任何不同。應(yīng)用程序可以通過(guò)重用的連接正常的訪問(wèn)數(shù)據(jù)庫(kù),進(jìn)行訪問(wèn)數(shù)據(jù)的操作,完成操作后應(yīng)顯式的調(diào)用close()關(guān)閉數(shù)據(jù)庫(kù)連接。
Connection con = ds.getConnection("User", "Pwd");
相關(guān)數(shù)據(jù)庫(kù)的操作;
con.close();
當(dāng)關(guān)閉數(shù)據(jù)連接后,當(dāng)前使用的數(shù)據(jù)庫(kù)連接將不會(huì)被物理關(guān)閉,而是放回到數(shù)據(jù)庫(kù)連接池中進(jìn)行重用。
JDBC3.0規(guī)范中數(shù)據(jù)庫(kù)連接池框架
JDBC3.0規(guī)范中通過(guò)提供了一個(gè)支持?jǐn)?shù)據(jù)庫(kù)連接池的框架,這個(gè)框架僅僅規(guī)定了如何支持連接池的實(shí)現(xiàn),而連接池的具體實(shí)現(xiàn)JDBC 3.0規(guī)范并沒(méi)有做相關(guān)的規(guī)定。通過(guò)這個(gè)框架可以讓不同角色的開(kāi)發(fā)人員共同實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池。
通過(guò)JDBC3.0規(guī)范可以知道具體數(shù)據(jù)庫(kù)連接池的實(shí)現(xiàn)可以分為JDBC Driver級(jí)和Application Server級(jí)。在JDBC Driver級(jí)的實(shí)現(xiàn)中任何相關(guān)的工作均由特定數(shù)據(jù)庫(kù)廠商的JDBC Drvier的開(kāi)發(fā)人員來(lái)具體實(shí)現(xiàn),即JDBC Driver既需要提供對(duì)數(shù)據(jù)庫(kù)連接池的支持同時(shí)也必須對(duì)數(shù)據(jù)庫(kù)連接池進(jìn)行具體實(shí)現(xiàn)。而在Application Server級(jí)中數(shù)據(jù)庫(kù)連接池的實(shí)現(xiàn)中特定數(shù)據(jù)庫(kù)廠商的JDBC Driver開(kāi)發(fā)人員和Application Server開(kāi)發(fā)人員來(lái)共同實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池的實(shí)現(xiàn)(但是現(xiàn)在大多數(shù)Application Server廠商實(shí)現(xiàn)的連接池的機(jī)制和規(guī)范中提到有差異),其中特定數(shù)據(jù)庫(kù)廠商的JDBC Driver提供數(shù)據(jù)庫(kù)連接池的支持而特定的Application Server廠商提供數(shù)據(jù)庫(kù)連接池的具體實(shí)現(xiàn)。
JDBC3.0規(guī)范規(guī)定了如下的類(lèi)和接口來(lái)支持?jǐn)?shù)據(jù)庫(kù)連接池的實(shí)現(xiàn)。
javax.sql.ConnectionEvent
javax.sql.ConnectionPoolDataSource
javax.sql.PooledConnection
javax.sql.ConnectionEventListener
其中除javax.sql.ConnectionEvent是類(lèi),其它的均為接口。
通過(guò)此圖可以大概的了解相關(guān)接口在一個(gè)典型的三層環(huán)境中應(yīng)用程序的位置。
數(shù)據(jù)庫(kù)連接池實(shí)現(xiàn)層次中,由特定數(shù)據(jù)庫(kù)廠商的JDBC Driver開(kāi)發(fā)人員提供連接池支持,而特定Application Server提供連接池實(shí)現(xiàn)的情況比較復(fù)雜,其它的實(shí)現(xiàn)層次均可視為其簡(jiǎn)化情況的一種。下面將針對(duì)這種情況進(jìn)行說(shuō)明。
在這個(gè)框架主要有兩個(gè)用戶角色存在,它們分別是:
特定數(shù)據(jù)庫(kù)廠商的JDBC Driver開(kāi)發(fā)人員,之后將簡(jiǎn)稱(chēng)為Driver Vendor
特定Application Server中連接池開(kāi)發(fā)人員,之后將簡(jiǎn)稱(chēng)為Pooling Vendor
下面對(duì)幾個(gè)關(guān)鍵模塊進(jìn)行詳細(xì)的說(shuō)明:
Driver Vendor DataSource:
Driver Vendor必須提供一個(gè)ConnectionPoolDataSource 接口的具體實(shí)現(xiàn),通過(guò)這個(gè)接口Pooling Vendor可以得到一個(gè)PooledConnection對(duì)象,從而使第三方實(shí)現(xiàn)的連接池可以使用特定數(shù)據(jù)庫(kù)廠商得到JDBC Driver產(chǎn)生的數(shù)據(jù)庫(kù)連接。在這里ConnectionPoolDataSource接口扮演的角色可以視為產(chǎn)生PooledConnection 對(duì)象的工廠。
Driver Vendor PooledConnection:
Driver Vendor必須提供標(biāo)準(zhǔn)PooledConnection 接口實(shí)現(xiàn)的類(lèi),這個(gè)接口允許Pooling Vendor在JDBC Driver提供連接池支持的基礎(chǔ)上實(shí)現(xiàn)連接池。一個(gè)具體PooledConnection對(duì)象代表了一個(gè)物理的數(shù)據(jù)庫(kù)連接;由PooledConnection對(duì)象創(chuàng)建Connection對(duì)象僅僅只是一個(gè)指向PooledConnetion對(duì)象的句柄。在JDBC 3.0連接池實(shí)現(xiàn)框架中PooledConnection對(duì)象扮演的角色可以視為產(chǎn)生Connection對(duì)象的工廠。
Pooling Vendor DataSource:
Pooling Vendor必須實(shí)現(xiàn)DataSource接口,這個(gè)接口是和連接池實(shí)現(xiàn)模塊進(jìn)行交互的入口點(diǎn)。ConnectionPoolDataSource根據(jù)需要?jiǎng)?chuàng)建PooledConnection對(duì)象。
Pooling Vendor Connection Cache:
此模塊是Pooling Vendor對(duì)連接池的具體實(shí)現(xiàn)。JDBC 3.0 規(guī)范沒(méi)有規(guī)定在DataSource對(duì)象和數(shù)據(jù)庫(kù)連接池實(shí)現(xiàn)之間的需要實(shí)現(xiàn)的接口,所以它們之間的交互由Pooling Vendor自己定義。一般而言,一個(gè)數(shù)據(jù)庫(kù)連接池的具體實(shí)現(xiàn)包含了一個(gè)或若干個(gè)具體的類(lèi),但是在連接池實(shí)現(xiàn)模塊中必須包含一個(gè)類(lèi)實(shí)現(xiàn)標(biāo)準(zhǔn)ConnectionEventListener接口。當(dāng)一個(gè)PooledConnectiond對(duì)象被關(guān)閉或者出現(xiàn)異常的時(shí)候,PooledConnection對(duì)象將會(huì)向ConnectionEventListener接口發(fā)送ConnectionEvent對(duì)象,連接池實(shí)現(xiàn)模塊將會(huì)根據(jù)返回的ConnectionEvent對(duì)象對(duì)PooledConnection進(jìn)行關(guān)閉或者重用操作。
ConnectionEvent:
實(shí)現(xiàn)連接池時(shí),當(dāng)應(yīng)用程序調(diào)用Connection.close()試圖去關(guān)閉數(shù)據(jù)庫(kù)連接時(shí),這時(shí)需要有一個(gè)通告給連接池實(shí)現(xiàn)模塊,通告對(duì)當(dāng)前的數(shù)據(jù)庫(kù)物理連接(PooledConnection 對(duì)象)進(jìn)行重用。為了使連接池實(shí)現(xiàn)模塊能得到這種"通告",連接池實(shí)現(xiàn)模塊必須實(shí)現(xiàn)ConnectionEventListener接口,而且同時(shí)需要注冊(cè)成為PooledConnection對(duì)象的監(jiān)聽(tīng)者。連接池實(shí)現(xiàn)模塊通過(guò)PooledConnection.addConnectionEventListener()方法注冊(cè)自己成為一個(gè)監(jiān)聽(tīng)者。
在典型三層環(huán)境中具體調(diào)用流程:
當(dāng)應(yīng)用程序通過(guò)調(diào)用DataSource.getConnection()得到一個(gè)數(shù)據(jù)庫(kù)連接。
Pooling Vendor實(shí)現(xiàn)的DataSource對(duì)象在連接池中進(jìn)行查找看當(dāng)前是否有有效的PooledConnection對(duì)象,如果連接池中有可用的PooledConnection,則進(jìn)行檢查,如果當(dāng)前的PooledConnection可用則使用。
如果如果連接池中沒(méi)有可用的PooledConnection對(duì)象,或者當(dāng)前的PooledConnection對(duì)象不正確,那么Pooling Vendor調(diào)用ConnectionPoolDataSource.getPooledConnection類(lèi)創(chuàng)建一個(gè)新的PooledConnection對(duì)象,這時(shí)由Driver Vendor實(shí)現(xiàn)的ConnectionPoolDataSource將會(huì)創(chuàng)建一個(gè)滿足要求新的PooledConnection對(duì)象,并將其返回給連接池實(shí)現(xiàn)模塊進(jìn)行管理。
然后,Pooling Vendor會(huì)調(diào)用PooledConnection.getConnection()獲得一個(gè)邏輯的Connection對(duì)象,這個(gè)邏輯的Connection對(duì)象將會(huì)象正常的Connection對(duì)象返回給應(yīng)用程序。這個(gè)邏輯Connection對(duì)象實(shí)際上是連接池中PooledConnection對(duì)象的一個(gè)句柄,當(dāng)連接池有效時(shí),應(yīng)用程序調(diào)用DataSource.getConnection()就會(huì)得到這個(gè)句柄。簡(jiǎn)而言之,應(yīng)用程序此時(shí)使用的Connection對(duì)象僅僅是其創(chuàng)建者PooledConnection對(duì)象的句柄而已。
連接池實(shí)現(xiàn)模塊調(diào)用PooledConnection.addConnectionEventListener()將自己注冊(cè)成為一個(gè)PooledConnection對(duì)象的監(jiān)聽(tīng)者,當(dāng)數(shù)據(jù)庫(kù)連接需要重用或者關(guān)閉的時(shí)候連接池實(shí)現(xiàn)模塊可以得到通告。
當(dāng)應(yīng)用程序通過(guò)調(diào)用Connection.close()來(lái)關(guān)閉數(shù)據(jù)庫(kù)連接,這時(shí)一個(gè)ConnectionEvent對(duì)象被創(chuàng)建并被返回到連接池實(shí)現(xiàn)模塊,連接池實(shí)現(xiàn)模塊接受到此通告后,將PooledConnection對(duì)象返回到池中進(jìn)行重用。這些過(guò)程中其它角色都不能訪問(wèn)PooledConnection.close()方法,能訪問(wèn)這個(gè)方法的只有Pooling Vendor,它們使用這個(gè)方法對(duì)連接池中的對(duì)象進(jìn)行操作,通過(guò)PooledConnection.close()方法可以關(guān)閉物理數(shù)據(jù)庫(kù)連接。
關(guān)于數(shù)據(jù)庫(kù)連接池的基本原理就為大家介紹這么多,希望對(duì)大家能夠有所幫助,上文中的內(nèi)容比較適合剛剛?cè)腴T(mén)的初學(xué)者學(xué)習(xí),希望大家不要錯(cuò)過(guò)這篇文章哦。