?
我們使用連接池訪問數據庫,是不是在關閉了connection之后它所屬的statement和result都會自動關閉了呢?就是說只需要關閉connection?
那么這樣的話是不是只要在try{...}catch{...}finnally{conn.close();}這樣的框架下工作就肯定是不會存在連接資源占用的情況了呢?
經過我在DB2上測試的情況是這樣的,不知道是不是不同的jdk或者as環境下會有所不同? 我一直無法完全肯定這一問題,請大家賜教!
本來我只要養成在關閉connection之前把其他對象一一關閉的習慣,就不會存在這個問題了,但是我為了偷懶起見,把簡單的查詢和更新操作封裝到了一個基類的公用函數中,方便隨時在代碼中調用,代碼如下:
public static ResultSet sysSelect(Connection conn,String sql)throws SQLException{
Statement st = null;
st = conn.createStatement();
return st.executeQuery(sql);
}
由于這樣的功能隨時隨地會被使用,這不得不使我考慮這個函數中產生中間對象statement st的生存期問題!*_*
-------------------------------------------------------------------------------------
是,最起碼數據庫端資源是得到了釋放
不過這也不絕對,因為statement,result對應的是curior,而有些
DB的curior是share的,
總之,關了connection就可以了
-------------------------------------------------------------------------------------
你是說:在有的時候就算是釋放了connection也可能有部分的Statement和ResultSet還占用著數據庫資源?
如果是這樣的話,那么在這個時候java和數據庫的連接雖然斷開了,可由于沒有在連接的時候預先釋放這些Statement和ResultSret資源(在數據庫方應該理解為游標或者其他資源),會影響數據庫的性能的吧?這似乎無以從程序上來證實!
-------------------------------------------------------------------------------------
> 你是說:在有的時候就算是釋放了connection也可能有部分的
> tatement和ResultSet還占用著數據庫資源?
不會,本地的statement和ResultSet會被回收,但數據庫端的資源不一定會釋放,但你不必擔心此事,應為這不是一件壞事,就像你用的數據庫連接池,數據庫也可以對curior做cache,畢竟頻繁創建curior對DB來說是比較expansive的.
當然,前提是數據庫share_curior參數的設置不是0
-------------------------------------------------------------------------------------
本地對象如果不被使用的確可以釋放,但tcp連接什么時候釋放是個問題,還有tcp連接和connection對應還是和resultset對應,這個似乎是未知的,不過看起來java里似乎是和connection對應的,否則只需要全局維護一個長連接connection對象就可以了,只考慮生成(是否可用)的問題而不用考慮關閉的問題(除非出于性能目的,可以考慮多連接加鎖的實現,也無需用戶去關閉),ado里面好像可以這么做。如果tcp連接過多,一定會把數據庫拖垮的。
至于conn, rs, st之間的依賴關系似乎也不明確,所以我在這個時候是自己寫一個resultset接口的實現,然后重載close方法,去一一關閉其依賴對象,甚至你可以在final函數里面關閉,不過后者看起來沒什么大用。
-------------------------------------------------------------------------------------
Statement也要Close,本人在使用中發現,若單單調用Connection.Close,實際上Connection并不會立即Close,可能要等到垃圾回收Statement后Connection才會Close, 本人使用的數據庫是SQLServer.
-------------------------------------------------------------------------------------
我曾試過將Connection對象作為一個static屬性放在一個類DBConnection中,然后,建立一個連接后,其它所有要用連接的地方就調用
DBConnection.connection,
最后再關掉,本以為這是一高招,但結果卻是不可行。connection被用一次后,調用DBConnection.connection時就不行了。
但我認為思路應該是對的,不知是否還有同志嘗試過
至于statement,要關掉并不難,可以在一個要連某個數據庫的類中將其可connection一同聲明為static,這樣在任何地方用完后,就可以很方便的關掉了
-------------------------------------------------------------------------------------
全部都要關閉,關閉connection后statement和result還是可以用的,可以在關閉connection后,再試試遍歷result還是可以得到數據的,result不關閉會導致游標超出范圍等錯誤
-------------------------------------------------------------------------------------
請問navyzhu和rtm: 你們是在什么數據庫上測試得到的這個結果的?
我在DB2和ORACLE8.1.2上測試出來的結果都很明顯地提示:SQLException:關閉的連接....(所有的conn,st,rs使用的時候是默認方式)
-------------------------------------------------------------------------------------
這么看來odbc/jdbc的設計現在也不太適宜了,為什么有conn, st的存在是因為希望可以復用,減少開銷,但增加了程序員的負擔,可能不多的幾行代碼,建立對象和關閉對象占了一半。甚至復用也很難實現,因為很多頁面中可能只訪問一兩次數據庫,如果沒有自己設計或者底層的連接池,頁面切換該消耗的還是要消耗。就算不是這樣,按照設計的一般標準,需要把粒度細化,這樣結果和上面一樣,如果要重用,必須附加上多余的耦合變量,更加不好。
反過來有了連接池,也基本上不需要conn,或者st,后者根據使用者的經驗,似乎可以表達為得到rs后就可以關閉或者直接重用。conn也是可以重用的(否則也就不能連接池了),不過大概需要在前一個返回的resultset被關閉之后,也就是我說的和tcp連接對應的概念,這個和ado不同。
-------------------------------------------------------------------------------------
Connection一旦關閉,所有由它打開的ResultSet不在可用。這在jdk文檔中有說明。否則,沒有必要定義javax.sql.RowSet了。
-------------------------------------------------------------------------------------
我用的數據庫是SQLServer2000,程序是一個數據月結程序,開始我發現月結有時成功,有時失敗,看了后臺才發現因為Statement沒有Close,所以Connection太多以至于打開幾百個Connection,結果處理失敗。
-------------------------------------------------------------------------------------
Statement,ResultSet肯定都是關閉了得,但是資源還沒有釋放阿。如果從性能方面考慮的話,一定的明顯的關閉Statement,ResultSet,還有兩點需要注意:
1,不要隱式產生Statement,ResultSet對象
2,關閉對象一定要在finally中關閉
-------------------------------------------------------------------------------------
JDBC3.0規范中提出了一個新的規范:把Statement也進行緩存(一般是應用服務器廠商實現),這樣就可以在不同的connection之間可以共享Statement。所以我個人以為,在釋放connection之前,最好先close Statement,這樣有利于statement的重用(而且這也是規范所推薦的比較好的編程習慣)。
-------------------------------------------------------------------------------------
Statement和PreparedStatement已經將數據庫操作封裝的很好了,所以我從來不再作一個類完成類似的操作。另外connection資源一定要用完就關。不要返回ResultSet對象,用RowSet代替。
-------------------------------------------------------------------------------------
我在oracle中,如果不關statement,最后oracle會報錯
ORA-1000: max cursor exceeded.
可以在 v$open_cursor中查詢未關閉的cursor.
最好還是用完就關。
-------------------------------------------------------------------------------------
有一點應該是統一的吧?那就是:當直接connection.close()的時候java端的st對象資源必定已經被釋放了,而在DB端相應的cursor資源也許不會馬上釋放,不同的DB廠商針對這有情況有不同的資源回收策略。
但問題是:是不是正如oldma所說的那樣,我們也可以把DB端的資源回收當作黑盒(或者一個能自我調整的容器)來看待,而不必去關心它的溢出或者阻塞???正如我們現在經常使用的連接池pool一樣!!
1)如果是這樣的,那么我們可以只處理connection的關閉,因為connection的關閉會自動引起java端的rs、st和pst的關閉,而在DB端,則有cache可以復用這些未被顯式釋放的cursor資源,使這些資源不至于被掛起;
2)如果不是這樣,那么我們必須手工關閉rs、st和pst這些與數據庫資源對應的對象來釋放DB端的資源,然后在最后關閉conn,因為DB不會聰明地調度這些被掛起的資源,不主動關閉它們會導致DB資源被耗盡。
通過回答這一問題,請大家告訴我,我在發帖的時候寫的那個函數在極度頻繁被調用時可行嗎?因為這個函數不處理任何close()操作,只是根據調用者的connection參數和String參數返回rs給調用者,在調用者的代碼的最后會有rs.close()和connection.close()操作,但是肯定不會有st.close()。 那么這個未被顯式地close()的st就是我一直不清楚的東西,在非常頻繁地被重復這樣調用(事實上這樣的調用就象out.print()一樣多)后,DB端會是怎樣一種情況?
-------------------------------------------------------------------------------------
對于頻繁調用的函數Connecton及Statement絕對要Close,正如其他網友所說,不同的數據庫可能有不同的實現,但考慮到重用及數據庫的移植等問題,還是Close吧。
-------------------------------------------------------------------------------------
那種方法肯定有問題,不要說服務器端有什么問題,光客戶端就可能出現問題,比如一次循環中,你打開很多次的ResultSet和Statement,這個對象不會立刻被釋放的。
還有我前面說了,幾種情況:
1、ResultSet不依賴于Connection和Statement,這樣你只需要一個Connection和一個Statement,這樣你每次可以充用這兩個對象,那么你只需要和Connection一樣加上對Statement的關閉就可以了,中間的使用者必須自己關閉ResultSet
2、ResultSet依賴于Connection和Statement,這樣你必須小心設計中間的過程,在同一個命名空間里不能同時建立兩個ResultSet對象。
3、ResultSet依賴于Statement對象,但不依賴于Connection對象,這樣,你不關閉Statement可能和第一種情況下不關閉ResultSet一樣。
-------------------------------------------------------------------------------------
事先聲明一句,我不常寫數據庫的代碼。
但是我有個疑惑,根據JAVA DOC,Connection、Statement、ResultSet應該都會在垃圾收集時自動被釋放。因此理論上他們不應該會存在資源泄漏問題。可能顯式的調用Connection的close能夠更快的釋放數據庫資源,但我認為使用Connection Pool應該是更有效率的做法。但對使用Connection Pool的情況下,是否必須顯式的close Connection?Statement和ResultSet應該沒有必要顯式的釋放吧,至少從來沒有聽說有Statement Pool和ResultSet Pool,這是否意味著他們并不使用DB資源呢?
一切只是我的推測,希望有高人能夠提出一個可信的測試方案來推翻或是驗證它。
-------------------------------------------------------------------------------------
規范說明: connection.close 自動關閉 Statement.close 自動導致 ResultSet 對象無效,注意只是 ResultSet 對象無效,ResultSet 所占用的資源可能還沒有釋放。所以還是應該顯式執行connection、Statement、ResultSet的close方法。特別是在使用connection pool的時候,connection.close 并不會導致物理連接的關閉,不執行ResultSet的close可能會導致更多的資源泄露。
摘錄自 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()會導致它所產生的resultset實例無效,也就是說方法返回參數的rs在該方法執行完畢的時候就是無效的。不知道我的想法對不對。
-------------------------------------------------------------------------------------
根據項目中的實際經驗,Statement應該顯式地關閉,或許與相應的數據庫廠商對數據庫資源和JDBC驅動實現有各自的不同,為了避免資源沒有被及時釋放,在connection關閉之前,顯式地關閉Statement是必要的。我的做法是將Connection對象做一個包裝,實際上是類似proxy,在獲取Statement的時候記錄其引用,然后在調用Connection的close方法的時候檢查記錄的引用中的Statement是否已經關閉,如果沒有關閉就調用其close方法關閉,根據實際應用的反饋,效果還不錯。
-------------------------------------------------------------------------------------
非常感謝大家在這里的建議! 雖然從程序上講這似乎有點兒鉆牛角尖,但這會幫助我理解在高訪問頻率情況下,代碼執行效率的巨大差別!
我個人認為:在一個大吞數據吐量和訪問率的信息系統中,其健壯性除了使用某種先進的API庫或者架構之外,關鍵之處在于代碼的編寫習慣、或者是我們經常使用到的、看似可有可無的某些代碼的編寫方式上!
to glassprogrammer: 關閉連接后還能使用rs的討論,我在其他地方看到過類似的討論,可是我以前在DB2、ORACLE812上測試的結果都是行不通的!不知道你是在哪里設置了這一特性? 不過對于使用conn.createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)的最后一個參數可以做到這一點! 但是這是jdbc3.0里的功能,我現在所在的都是IBM的環境,IBM自帶的JDK只相當于sun jre1.3.1的版本,是沒有這個功能所以也無法測試。
-------------------------------------------------------------------------------------
根據測試,關不關Statement, ResultSet無所謂,只要Connection關閉,自動就關閉掉前兩者,如果說Statement, ResultSet沒關閉,會使DB端資源不能釋放,但Connection.close()事件里就調用了Statement.close()->ResultSet.close(),你再調用Statement.close(),ResultSet.close()一次干什么了?效果不是一樣碼?至于cursor,我測試了Oracle,SQL2000,只要Connection.close,Cursor就也自動關閉了.
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();
}
執行后,在SQL查詢分析器里sp_who,不占連接啊.
-------------------------------------------------------------------------------------
不好意思,實際上Connection.close只是斷開同DB的連接,它實際并沒去關閉Statement, ResultSet。之所以說Connection.close后,ResultSet無效,是因為ResultSet的操作之前,會檢查連接是否有效。所以如next,first等操作都會出錯。其他一些跟數據無關的操作就不會有問題。
一定要close掉ResultSet,否則資源會泄漏,因為ResultSet里有Stream對象,close才會調用Stream.close。至于mysapphire的問題,一個簡單的方法就是再調用一下ResultSet.getStatement().close就可以了。不知道其他高手有何其他高見。
-------------------------------------------------------------------------------------
前兩天兩個Bea公司的來做技術支持,說我們的代碼有問題導致了內存泄露,讓我們在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.垃圾回收機制可以自動關閉它們
2.Statement關閉會導致ResultSet關閉
3.Connection關閉不會(?)導致Statement關閉
4.由于垃圾回收的線程級別是最低的,為了充分利用數據庫資源,有必要顯式關閉它們,尤其是使用Connection Pool的時候。
5.最優經驗是按照ResultSet,Statement,Connection的順序執行close
6.如果一定要傳遞ResultSet,應該使用RowSet,RowSet可以不依賴于Connection和Statement。Java傳遞的是引用,所以如果傳遞ResultSet,你會不知道Statement和Connection何時關閉,不知道ResultSet何時有效。
-------------------------------------------------------------------------------------
我是這樣處理的:在 DAO Factory 里放上這個方法。BaseImpl 即所有DAO 接口對象的實現的基類里,調用這個方法。另外還有 getConnection 的。這樣,在我的每個 Impl 類中,在用完這些資源后,即 try 的最后,都直接使用 close(rs, pst, conn) 來關閉相關數據庫資源(因為是傳引用哦,所以我是覺得可以這樣來減少代碼)。getConnection() 是直接得到連接啦。
請大家幫我評判一下,這樣處理有問題嗎?
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 里,不知道基于什么考慮。
數據庫資源與普通 Object 不一樣的。應該說建議還是要即時、完全地關閉,也有利于 pooling 來工作。 我想是這樣,應該說你的 close() 是由 pooling 的實現來?BR>
-------------------------------------------------------------------------------------
其實好像這是一個很普遍的誤解。好多老手,高手,都想當然地rs=null。
有些jvm的實現,局部block out of scope時,局部變量引用的對象并沒有變成gc'able。
不過,函數退出時是沒問題的。不需要rs=null
rs=null的不好之處在于,一,麻煩。二,無法用final,而final對提高程序質量還是很有意義的。
對這個資源問題,我前幾天在搞vb4(慚愧呀,都老掉牙了!)的時候,也遇到了。驚人地一致,我也是想做這么一個公用函數,來封裝常見的stored proc調用。查了好久msdn也沒查出個所以然來。測試倒是沒問題。好在那個程序就是客戶端的,沒有server端那么頻繁調用。
但是在vb里有問題的,到java里是可以輕松解決的。
單純從問題本身,我認為這是標準所沒有規定的。(connection關閉后Statement不可用不表示資源必然被回收)。標準沒有的,各個driver的實現就可能不同。所以,無論實驗結果如何,都是不可依賴的。
解決方法應該是象前面一位老兄說的,自己封裝ResultSet。
感謝jdbc的設計者,ResultSet是一個接口。我們大可以自己實現ResultSet,在close里面同時關上Statement。
為了避免寫好多委托函數的麻煩(ResultSet的方法好多啊!不同的jdbc版本還不一樣),可以用dynamic proxy。(具體方法我本來寫了一下,但是剛才提交的時候,session timeout,重新登陸后,我寫的東西都不見了!懶得再寫了)。
這樣在你最原始的那個函數里,你可以這樣用:
Statement stmt = ...;
boolean ok = false;
try{
? ResultSet rs = ...;
? ok =
true;
?
return MyResultWrapper.instance(rs,stmt);
}
finally{
?
if(!ok)stmt.close();
}
不過,越過這個問題本身。我覺得,返回ResultSet也許不總是一個好的選擇。為什么不接受一個callback呢?這樣,你根本不用要求客戶代碼記得調用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的需求夠用了。而且資源管理更健壯。
-------------------------------------------------------------------------------------
最好的辦法是,在執行Connection類方法close()時,同時關閉Statement,可以考慮使用Delegate模式,以犧牲部分的性能來換取穩定;
-------------------------------------------------------------------------------------
我覺得Connection這樣的對象本身就是一種封裝,不同的廠商實現不同,如果使用了某個廠商的連接池,比如weblogic的,那么Connection.close()并沒有釋放這個conn,而是將它重新放入池中。因為從datasource中取出的conn實際上是Connection的子類,它覆蓋了close()方法。具體可以查看相關廠商提供的文檔。
我想這是為何有的人一定要顯示關閉statement,有的人又發現不顯示關閉statement也可以的原因吧。