JAVA & XML & JAVASCRIPT & AJAX & CSS

Web 2.0 技术储备............

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  77 随笔 :: 17 文章 :: 116 评论 :: 0 Trackbacks
 
我们使用连接池访问数据库,是不是在关闭了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也可以的原因吧。
posted on 2006-09-01 09:37 Web 2.0 技术资源 阅读(3727) 评论(2)  编辑  收藏 所属分类: 数据库

评论

# re: 关于 JDBC 资源回收的讨论 ! 2006-09-01 11:04 바보
我觉得我页应该说两句,一般我们都是在一个程序里面使用一个conn,多个stmt,多个rs.所以保证了在一个程序的conn的获得和释放是一致的.~但是rs和stmt一般都没处理~~看来我埋下了一颗定时炸弹啊~~
ps,哪里又比较系统的关于Statement和ResultSet的资料?  回复  更多评论
  

# re: 关于 JDBC 资源回收的讨论 ! 2009-03-11 08:40
晕。潮汐连个来源都不写  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: