?
我們使用連接池訪問數(shù)據(jù)庫,是不是在關(guān)閉了connection之后它所屬的statement和result都會自動關(guān)閉了呢?就是說只需要關(guān)閉connection?
那么這樣的話是不是只要在try{...}catch{...}finnally{conn.close();}這樣的框架下工作就肯定是不會存在連接資源占用的情況了呢?
經(jīng)過我在DB2上測試的情況是這樣的,不知道是不是不同的jdk或者as環(huán)境下會有所不同? 我一直無法完全肯定這一問題,請大家賜教!
本來我只要養(yǎng)成在關(guān)閉connection之前把其他對象一一關(guān)閉的習(xí)慣,就不會存在這個問題了,但是我為了偷懶起見,把簡單的查詢和更新操作封裝到了一個基類的公用函數(shù)中,方便隨時在代碼中調(diào)用,代碼如下:
public static ResultSet sysSelect(Connection conn,String sql)throws SQLException{
Statement st = null;
st = conn.createStatement();
return st.executeQuery(sql);
}
由于這樣的功能隨時隨地會被使用,這不得不使我考慮這個函數(shù)中產(chǎn)生中間對象statement st的生存期問題!*_*
-------------------------------------------------------------------------------------
是,最起碼數(shù)據(jù)庫端資源是得到了釋放
不過這也不絕對,因為statement,result對應(yīng)的是curior,而有些
DB的curior是share的,
總之,關(guān)了connection就可以了
-------------------------------------------------------------------------------------
你是說:在有的時候就算是釋放了connection也可能有部分的Statement和ResultSet還占用著數(shù)據(jù)庫資源?
如果是這樣的話,那么在這個時候java和數(shù)據(jù)庫的連接雖然斷開了,可由于沒有在連接的時候預(yù)先釋放這些Statement和ResultSret資源(在數(shù)據(jù)庫方應(yīng)該理解為游標(biāo)或者其他資源),會影響數(shù)據(jù)庫的性能的吧?這似乎無以從程序上來證實!
-------------------------------------------------------------------------------------
> 你是說:在有的時候就算是釋放了connection也可能有部分的
> tatement和ResultSet還占用著數(shù)據(jù)庫資源?
不會,本地的statement和ResultSet會被回收,但數(shù)據(jù)庫端的資源不一定會釋放,但你不必擔(dān)心此事,應(yīng)為這不是一件壞事,就像你用的數(shù)據(jù)庫連接池,數(shù)據(jù)庫也可以對curior做cache,畢竟頻繁創(chuàng)建curior對DB來說是比較expansive的.
當(dāng)然,前提是數(shù)據(jù)庫share_curior參數(shù)的設(shè)置不是0
-------------------------------------------------------------------------------------
本地對象如果不被使用的確可以釋放,但tcp連接什么時候釋放是個問題,還有tcp連接和connection對應(yīng)還是和resultset對應(yīng),這個似乎是未知的,不過看起來java里似乎是和connection對應(yīng)的,否則只需要全局維護一個長連接connection對象就可以了,只考慮生成(是否可用)的問題而不用考慮關(guān)閉的問題(除非出于性能目的,可以考慮多連接加鎖的實現(xiàn),也無需用戶去關(guān)閉),ado里面好像可以這么做。如果tcp連接過多,一定會把數(shù)據(jù)庫拖垮的。
至于conn, rs, st之間的依賴關(guān)系似乎也不明確,所以我在這個時候是自己寫一個resultset接口的實現(xiàn),然后重載close方法,去一一關(guān)閉其依賴對象,甚至你可以在final函數(shù)里面關(guān)閉,不過后者看起來沒什么大用。
-------------------------------------------------------------------------------------
Statement也要Close,本人在使用中發(fā)現(xiàn),若單單調(diào)用Connection.Close,實際上Connection并不會立即Close,可能要等到垃圾回收Statement后Connection才會Close, 本人使用的數(shù)據(jù)庫是SQLServer.
-------------------------------------------------------------------------------------
我曾試過將Connection對象作為一個static屬性放在一個類DBConnection中,然后,建立一個連接后,其它所有要用連接的地方就調(diào)用
DBConnection.connection,
最后再關(guān)掉,本以為這是一高招,但結(jié)果卻是不可行。connection被用一次后,調(diào)用DBConnection.connection時就不行了。
但我認為思路應(yīng)該是對的,不知是否還有同志嘗試過
至于statement,要關(guān)掉并不難,可以在一個要連某個數(shù)據(jù)庫的類中將其可connection一同聲明為static,這樣在任何地方用完后,就可以很方便的關(guān)掉了
-------------------------------------------------------------------------------------
全部都要關(guān)閉,關(guān)閉connection后statement和result還是可以用的,可以在關(guān)閉connection后,再試試遍歷result還是可以得到數(shù)據(jù)的,result不關(guān)閉會導(dǎo)致游標(biāo)超出范圍等錯誤
-------------------------------------------------------------------------------------
請問navyzhu和rtm: 你們是在什么數(shù)據(jù)庫上測試得到的這個結(jié)果的?
我在DB2和ORACLE8.1.2上測試出來的結(jié)果都很明顯地提示:SQLException:關(guān)閉的連接....(所有的conn,st,rs使用的時候是默認方式)
-------------------------------------------------------------------------------------
這么看來odbc/jdbc的設(shè)計現(xiàn)在也不太適宜了,為什么有conn, st的存在是因為希望可以復(fù)用,減少開銷,但增加了程序員的負擔(dān),可能不多的幾行代碼,建立對象和關(guān)閉對象占了一半。甚至復(fù)用也很難實現(xiàn),因為很多頁面中可能只訪問一兩次數(shù)據(jù)庫,如果沒有自己設(shè)計或者底層的連接池,頁面切換該消耗的還是要消耗。就算不是這樣,按照設(shè)計的一般標(biāo)準(zhǔn),需要把粒度細化,這樣結(jié)果和上面一樣,如果要重用,必須附加上多余的耦合變量,更加不好。
反過來有了連接池,也基本上不需要conn,或者st,后者根據(jù)使用者的經(jīng)驗,似乎可以表達為得到rs后就可以關(guān)閉或者直接重用。conn也是可以重用的(否則也就不能連接池了),不過大概需要在前一個返回的resultset被關(guān)閉之后,也就是我說的和tcp連接對應(yīng)的概念,這個和ado不同。
-------------------------------------------------------------------------------------
Connection一旦關(guān)閉,所有由它打開的ResultSet不在可用。這在jdk文檔中有說明。否則,沒有必要定義javax.sql.RowSet了。
-------------------------------------------------------------------------------------
我用的數(shù)據(jù)庫是SQLServer2000,程序是一個數(shù)據(jù)月結(jié)程序,開始我發(fā)現(xiàn)月結(jié)有時成功,有時失敗,看了后臺才發(fā)現(xiàn)因為Statement沒有Close,所以Connection太多以至于打開幾百個Connection,結(jié)果處理失敗。
-------------------------------------------------------------------------------------
Statement,ResultSet肯定都是關(guān)閉了得,但是資源還沒有釋放阿。如果從性能方面考慮的話,一定的明顯的關(guān)閉Statement,ResultSet,還有兩點需要注意:
1,不要隱式產(chǎn)生Statement,ResultSet對象
2,關(guān)閉對象一定要在finally中關(guān)閉
-------------------------------------------------------------------------------------
JDBC3.0規(guī)范中提出了一個新的規(guī)范:把Statement也進行緩存(一般是應(yīng)用服務(wù)器廠商實現(xiàn)),這樣就可以在不同的connection之間可以共享Statement。所以我個人以為,在釋放connection之前,最好先close Statement,這樣有利于statement的重用(而且這也是規(guī)范所推薦的比較好的編程習(xí)慣)。
-------------------------------------------------------------------------------------
Statement和PreparedStatement已經(jīng)將數(shù)據(jù)庫操作封裝的很好了,所以我從來不再作一個類完成類似的操作。另外connection資源一定要用完就關(guān)。不要返回ResultSet對象,用RowSet代替。
-------------------------------------------------------------------------------------
我在oracle中,如果不關(guān)statement,最后oracle會報錯
ORA-1000: max cursor exceeded.
可以在 v$open_cursor中查詢未關(guān)閉的cursor.
最好還是用完就關(guān)。
-------------------------------------------------------------------------------------
有一點應(yīng)該是統(tǒng)一的吧?那就是:當(dāng)直接connection.close()的時候java端的st對象資源必定已經(jīng)被釋放了,而在DB端相應(yīng)的cursor資源也許不會馬上釋放,不同的DB廠商針對這有情況有不同的資源回收策略。
但問題是:是不是正如oldma所說的那樣,我們也可以把DB端的資源回收當(dāng)作黑盒(或者一個能自我調(diào)整的容器)來看待,而不必去關(guān)心它的溢出或者阻塞???正如我們現(xiàn)在經(jīng)常使用的連接池pool一樣!!
1)如果是這樣的,那么我們可以只處理connection的關(guān)閉,因為connection的關(guān)閉會自動引起java端的rs、st和pst的關(guān)閉,而在DB端,則有cache可以復(fù)用這些未被顯式釋放的cursor資源,使這些資源不至于被掛起;
2)如果不是這樣,那么我們必須手工關(guān)閉rs、st和pst這些與數(shù)據(jù)庫資源對應(yīng)的對象來釋放DB端的資源,然后在最后關(guān)閉conn,因為DB不會聰明地調(diào)度這些被掛起的資源,不主動關(guān)閉它們會導(dǎo)致DB資源被耗盡。
通過回答這一問題,請大家告訴我,我在發(fā)帖的時候?qū)懙哪莻€函數(shù)在極度頻繁被調(diào)用時可行嗎?因為這個函數(shù)不處理任何close()操作,只是根據(jù)調(diào)用者的connection參數(shù)和String參數(shù)返回rs給調(diào)用者,在調(diào)用者的代碼的最后會有rs.close()和connection.close()操作,但是肯定不會有st.close()。 那么這個未被顯式地close()的st就是我一直不清楚的東西,在非常頻繁地被重復(fù)這樣調(diào)用(事實上這樣的調(diào)用就象out.print()一樣多)后,DB端會是怎樣一種情況?
-------------------------------------------------------------------------------------
對于頻繁調(diào)用的函數(shù)Connecton及Statement絕對要Close,正如其他網(wǎng)友所說,不同的數(shù)據(jù)庫可能有不同的實現(xiàn),但考慮到重用及數(shù)據(jù)庫的移植等問題,還是Close吧。
-------------------------------------------------------------------------------------
那種方法肯定有問題,不要說服務(wù)器端有什么問題,光客戶端就可能出現(xiàn)問題,比如一次循環(huán)中,你打開很多次的ResultSet和Statement,這個對象不會立刻被釋放的。
還有我前面說了,幾種情況:
1、ResultSet不依賴于Connection和Statement,這樣你只需要一個Connection和一個Statement,這樣你每次可以充用這兩個對象,那么你只需要和Connection一樣加上對Statement的關(guān)閉就可以了,中間的使用者必須自己關(guān)閉ResultSet
2、ResultSet依賴于Connection和Statement,這樣你必須小心設(shè)計中間的過程,在同一個命名空間里不能同時建立兩個ResultSet對象。
3、ResultSet依賴于Statement對象,但不依賴于Connection對象,這樣,你不關(guān)閉Statement可能和第一種情況下不關(guān)閉ResultSet一樣。
-------------------------------------------------------------------------------------
事先聲明一句,我不常寫數(shù)據(jù)庫的代碼。
但是我有個疑惑,根據(jù)JAVA DOC,Connection、Statement、ResultSet應(yīng)該都會在垃圾收集時自動被釋放。因此理論上他們不應(yīng)該會存在資源泄漏問題。可能顯式的調(diào)用Connection的close能夠更快的釋放數(shù)據(jù)庫資源,但我認為使用Connection Pool應(yīng)該是更有效率的做法。但對使用Connection Pool的情況下,是否必須顯式的close Connection?Statement和ResultSet應(yīng)該沒有必要顯式的釋放吧,至少從來沒有聽說有Statement Pool和ResultSet Pool,這是否意味著他們并不使用DB資源呢?
一切只是我的推測,希望有高人能夠提出一個可信的測試方案來推翻或是驗證它。
-------------------------------------------------------------------------------------
規(guī)范說明: connection.close 自動關(guān)閉 Statement.close 自動導(dǎo)致 ResultSet 對象無效,注意只是 ResultSet 對象無效,ResultSet 所占用的資源可能還沒有釋放。所以還是應(yīng)該顯式執(zhí)行connection、Statement、ResultSet的close方法。特別是在使用connection pool的時候,connection.close 并不會導(dǎo)致物理連接的關(guān)閉,不執(zhí)行ResultSet的close可能會導(dǎo)致更多的資源泄露。
摘錄自 JDBC. 3.0 Specification,13.1.3 Closing Statement Objects
An application calls the method Statement.close to indicate that it has finished processing a statement. All Statement objects will be closed when the connection that created them is closed. However, it is good coding practice for applications to close statements as soon as they have finished processing them. This allows any external resources that the statement is using to be released immediately.
Closing a Statement object will close and invalidate any instances of ResultSet produced by that Statement object. The resources held by the ResultSet object may not be released until garbage collection runs again, so it is a good practice to explicitly close ResultSet objects when they are no longer needed.
These comments about closing Statement objects apply to PreparedStatement and CallableStatement objects as well.
-------------------------------------------------------------------------------------
我覺得你這個方法這樣寫才比較合理:
public static ResultSet sysSelect(Connection conn,String sql)throws SQLException{
Statement st = null;
try{
st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
return rs;
}catch (SQLException ex){
throw ex;
}finally{
if (null!=st) st.close();
}
}
-------------------------------------------------------------------------------------
to glassprogrammer,你說的方法做過測試么,我覺得從原理上說不通。
finally中的st.close()會導(dǎo)致它所產(chǎn)生的resultset實例無效,也就是說方法返回參數(shù)的rs在該方法執(zhí)行完畢的時候就是無效的。不知道我的想法對不對。
-------------------------------------------------------------------------------------
根據(jù)項目中的實際經(jīng)驗,Statement應(yīng)該顯式地關(guān)閉,或許與相應(yīng)的數(shù)據(jù)庫廠商對數(shù)據(jù)庫資源和JDBC驅(qū)動實現(xiàn)有各自的不同,為了避免資源沒有被及時釋放,在connection關(guān)閉之前,顯式地關(guān)閉Statement是必要的。我的做法是將Connection對象做一個包裝,實際上是類似proxy,在獲取Statement的時候記錄其引用,然后在調(diào)用Connection的close方法的時候檢查記錄的引用中的Statement是否已經(jīng)關(guān)閉,如果沒有關(guān)閉就調(diào)用其close方法關(guān)閉,根據(jù)實際應(yīng)用的反饋,效果還不錯。
-------------------------------------------------------------------------------------
非常感謝大家在這里的建議! 雖然從程序上講這似乎有點兒鉆牛角尖,但這會幫助我理解在高訪問頻率情況下,代碼執(zhí)行效率的巨大差別!
我個人認為:在一個大吞數(shù)據(jù)吐量和訪問率的信息系統(tǒng)中,其健壯性除了使用某種先進的API庫或者架構(gòu)之外,關(guān)鍵之處在于代碼的編寫習(xí)慣、或者是我們經(jīng)常使用到的、看似可有可無的某些代碼的編寫方式上!
to glassprogrammer: 關(guān)閉連接后還能使用rs的討論,我在其他地方看到過類似的討論,可是我以前在DB2、ORACLE812上測試的結(jié)果都是行不通的!不知道你是在哪里設(shè)置了這一特性? 不過對于使用conn.createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)的最后一個參數(shù)可以做到這一點! 但是這是jdbc3.0里的功能,我現(xiàn)在所在的都是IBM的環(huán)境,IBM自帶的JDK只相當(dāng)于sun jre1.3.1的版本,是沒有這個功能所以也無法測試。
-------------------------------------------------------------------------------------
根據(jù)測試,關(guān)不關(guān)Statement, ResultSet無所謂,只要Connection關(guān)閉,自動就關(guān)閉掉前兩者,如果說Statement, ResultSet沒關(guān)閉,會使DB端資源不能釋放,但Connection.close()事件里就調(diào)用了Statement.close()->ResultSet.close(),你再調(diào)用Statement.close(),ResultSet.close()一次干什么了?效果不是一樣碼?至于cursor,我測試了Oracle,SQL2000,只要Connection.close,Cursor就也自動關(guān)閉了.
to navyzhu :你肯定其他地方有問題.我做了試驗
try{
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
for (int i=0; i<100; i++){
Connection conn = DriverManager.getConnection(
"jdbc:microsoft:sqlserver://Akun:1433;databaseName=bookstore", "sa",
"");
Statement stm = conn.createStatement();
ResultSet rs = stm.executeQuery("Select * from Book");
//stm.close();
conn.close();
}
執(zhí)行后,在SQL查詢分析器里sp_who,不占連接啊.
-------------------------------------------------------------------------------------
不好意思,實際上Connection.close只是斷開同DB的連接,它實際并沒去關(guān)閉Statement, ResultSet。之所以說Connection.close后,ResultSet無效,是因為ResultSet的操作之前,會檢查連接是否有效。所以如next,first等操作都會出錯。其他一些跟數(shù)據(jù)無關(guān)的操作就不會有問題。
一定要close掉ResultSet,否則資源會泄漏,因為ResultSet里有Stream對象,close才會調(diào)用Stream.close。至于mysapphire的問題,一個簡單的方法就是再調(diào)用一下ResultSet.getStatement().close就可以了。不知道其他高手有何其他高見。
-------------------------------------------------------------------------------------
前兩天兩個Bea公司的來做技術(shù)支持,說我們的代碼有問題導(dǎo)致了內(nèi)存泄露,讓我們在rs.close()和stmt.close()后面一定要加上rs = null和stmt = null。
爆寒!
-------------------------------------------------------------------------------------
Note: A ResultSet object is automatically closed by the Statement object that generated it when that Statement object is closed, re-executed, or is used to retrieve the next result from a sequence of multiple results. A ResultSet object is also automatically closed when it is garbage collected.
Note: A Statement object is automatically closed when it is garbage collected. When a Statement object is closed, its current ResultSet object, if one exists, is also closed.
Note: A Connection object is automatically closed when it is garbage collected. Certain fatal errors also close a Connection object.
摘自JDK1.4
可見
1.垃圾回收機制可以自動關(guān)閉它們
2.Statement關(guān)閉會導(dǎo)致ResultSet關(guān)閉
3.Connection關(guān)閉不會(?)導(dǎo)致Statement關(guān)閉
4.由于垃圾回收的線程級別是最低的,為了充分利用數(shù)據(jù)庫資源,有必要顯式關(guān)閉它們,尤其是使用Connection Pool的時候。
5.最優(yōu)經(jīng)驗是按照ResultSet,Statement,Connection的順序執(zhí)行close
6.如果一定要傳遞ResultSet,應(yīng)該使用RowSet,RowSet可以不依賴于Connection和Statement。Java傳遞的是引用,所以如果傳遞ResultSet,你會不知道Statement和Connection何時關(guān)閉,不知道ResultSet何時有效。
-------------------------------------------------------------------------------------
我是這樣處理的:在 DAO Factory 里放上這個方法。BaseImpl 即所有DAO 接口對象的實現(xiàn)的基類里,調(diào)用這個方法。另外還有 getConnection 的。這樣,在我的每個 Impl 類中,在用完這些資源后,即 try 的最后,都直接使用 close(rs, pst, conn) 來關(guān)閉相關(guān)數(shù)據(jù)庫資源(因為是傳引用哦,所以我是覺得可以這樣來減少代碼)。getConnection() 是直接得到連接啦。
請大家?guī)臀以u判一下,這樣處理有問題嗎?
public
static
void close(java.sql.ResultSet rs,
java.sql.PreparedStatement pst,
java.sql.Connection conn
) {
if (rs != null) {
try {
rs.close();
rs = null;
} catch (SQLException se) {
se.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException se) {
se.printStackTrace();
}
rs = null;
}
}
}
if (pst != null) {
try {
pst.close();
pst = null;
} catch (SQLException ex) {
ex.printStackTrace();
} finally {
if (pst != null) {
try {
pst.close();
} catch (SQLException se) {
se.printStackTrace();
}
pst = null;
}
}
}
if (conn != null) {
try {
conn.close();
conn = null;
} catch (SQLException ex) {
ex.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException se) {
se.printStackTrace();
}
conn = null;
}
}
}
}
</code>
上面有人說 close 方法必須放在 finally 里,不知道基于什么考慮。
數(shù)據(jù)庫資源與普通 Object 不一樣的。應(yīng)該說建議還是要即時、完全地關(guān)閉,也有利于 pooling 來工作。 我想是這樣,應(yīng)該說你的 close() 是由 pooling 的實現(xiàn)來?BR>
-------------------------------------------------------------------------------------
其實好像這是一個很普遍的誤解。好多老手,高手,都想當(dāng)然地rs=null。
有些jvm的實現(xiàn),局部block out of scope時,局部變量引用的對象并沒有變成gc'able。
不過,函數(shù)退出時是沒問題的。不需要rs=null
rs=null的不好之處在于,一,麻煩。二,無法用final,而final對提高程序質(zhì)量還是很有意義的。
對這個資源問題,我前幾天在搞vb4(慚愧呀,都老掉牙了!)的時候,也遇到了。驚人地一致,我也是想做這么一個公用函數(shù),來封裝常見的stored proc調(diào)用。查了好久msdn也沒查出個所以然來。測試倒是沒問題。好在那個程序就是客戶端的,沒有server端那么頻繁調(diào)用。
但是在vb里有問題的,到j(luò)ava里是可以輕松解決的。
單純從問題本身,我認為這是標(biāo)準(zhǔn)所沒有規(guī)定的。(connection關(guān)閉后Statement不可用不表示資源必然被回收)。標(biāo)準(zhǔn)沒有的,各個driver的實現(xiàn)就可能不同。所以,無論實驗結(jié)果如何,都是不可依賴的。
解決方法應(yīng)該是象前面一位老兄說的,自己封裝ResultSet。
感謝jdbc的設(shè)計者,ResultSet是一個接口。我們大可以自己實現(xiàn)ResultSet,在close里面同時關(guān)上Statement。
為了避免寫好多委托函數(shù)的麻煩(ResultSet的方法好多啊!不同的jdbc版本還不一樣),可以用dynamic proxy。(具體方法我本來寫了一下,但是剛才提交的時候,session timeout,重新登陸后,我寫的東西都不見了!懶得再寫了)。
這樣在你最原始的那個函數(shù)里,你可以這樣用:
Statement stmt = ...;
boolean ok = false;
try{
? ResultSet rs = ...;
? ok =
true;
?
return MyResultWrapper.instance(rs,stmt);
}
finally{
?
if(!ok)stmt.close();
}
不過,越過這個問題本身。我覺得,返回ResultSet也許不總是一個好的選擇。為什么不接受一個callback呢?這樣,你根本不用要求客戶代碼記得調(diào)用close(),一切你都處理好了。我在以前的一個帖子中也提過這種方法:
interface ResultListener{
publicvoid acceptResult(ResultRow rs);
}
void populate(ResultListener l){
final Statement stmt = ...;
try{
final ResultSet rs = ...;
try{
while(...){l.acceptResult(new ResultRow(rs));}
}
finally{
rs.close();
}
}
finally{
stmt.close();
}
}
個人覺得,這種方法對不需要update,不需要在result set上隨機移動cursor的需求夠用了。而且資源管理更健壯。
-------------------------------------------------------------------------------------
最好的辦法是,在執(zhí)行Connection類方法close()時,同時關(guān)閉Statement,可以考慮使用Delegate模式,以犧牲部分的性能來換取穩(wěn)定;
-------------------------------------------------------------------------------------
我覺得Connection這樣的對象本身就是一種封裝,不同的廠商實現(xiàn)不同,如果使用了某個廠商的連接池,比如weblogic的,那么Connection.close()并沒有釋放這個conn,而是將它重新放入池中。因為從datasource中取出的conn實際上是Connection的子類,它覆蓋了close()方法。具體可以查看相關(guān)廠商提供的文檔。
我想這是為何有的人一定要顯示關(guān)閉statement,有的人又發(fā)現(xiàn)不顯示關(guān)閉statement也可以的原因吧。