今天在运行一个以前一直被注释掉的监听器以后不久,程序报"[Microsoft][SQLServer 2000 Driver for JDBC]Object has been closed."异常,决定查查这个原因。
调查的结果是一个很让人想骂人的脑残问题。
自己写了个简单的测试例子来重现这个问题:
package
com.marion.test;
import
java.sql.Connection;
import
java.sql.DriverManager;
import
java.sql.PreparedStatement;
import
java.sql.ResultSet;
import
java.sql.SQLException;
/**
* 测试在很多访问数据库的类中频发的Object has been closed异常的发生原因
*
@author
Marion
*
*/
public
class
Test {
public
static
void
main(String[] args) {
try
{
XManager xManager
=
new
XManager();
xManager.doSQL1();
xManager.doSQL2();
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
class
XManager{
Connection conn;
public
XManager(){
try
{
String url
=
"
jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=bt_jszx;SelectMethod=Cursor
"
;
Class.forName(
"
com.microsoft.jdbc.sqlserver.SQLServerDriver
"
);
conn
=
DriverManager.getConnection(url,
"
sa
"
,
"
sa
"
);
}
catch
(Exception e) {
e.printStackTrace();
}
}
public
void
doSQL1(){
try
{
PreparedStatement ps
=
conn.prepareStatement(
"
select * from bt_user
"
);
ResultSet rs
=
ps.executeQuery();
while
(rs.next()){
//
do nothing
}
}
catch
(SQLException e) {
e.printStackTrace();
}
finally
{
try
{
if
(
!
conn.isClosed()){
conn.close();
}
}
catch
(SQLException e) {
e.printStackTrace();
}
}
}
public
void
doSQL2(){
try
{
PreparedStatement ps
=
conn.prepareStatement(
"
select * from bt_param
"
);
ResultSet rs
=
ps.executeQuery();
while
(rs.next()){
//
do nothing
}
}
catch
(SQLException e) {
e.printStackTrace();
}
finally
{
try
{
if
(
!
conn.isClosed()){
conn.close();
}
}
catch
(SQLException e) {
e.printStackTrace();
}
}
}
}
上面的代码在执行以后报:
java.sql.SQLException: [Microsoft][SQLServer 2000 Driver for JDBC]Object has been closed.
at com.microsoft.jdbc.base.BaseExceptions.createException(Unknown Source)
at com.microsoft.jdbc.base.BaseExceptions.getException(Unknown Source)
at com.microsoft.jdbc.base.BaseConnection.validateClosedState(Unknown Source)
at com.microsoft.jdbc.base.BaseConnection.prepareStatement(Unknown Source)
at com.microsoft.jdbc.base.BaseConnection.prepareStatement(Unknown Source)
at com.marion.test.XManager.doSQL2(Test.java:57)
at com.marion.test.Test.main(Test.java:19)
在老程序的许多某某Manager类里都有类似这样的多个方法共用一个Connection对象的情况,当你每次想使用这些方法都重新new一个Manager对象去调用的时候,并不会出现这个问题。但由于程序历经多年多人维护,在一段程序中重复使用同一个Manager对象的情况在所难免,于是,问题出现了。
解决这个问题,就是在每个方法中都重新new一个Connection对象,在使用完毕后手动关闭。
当年定义这个Manager类的人明显欠考虑,再加上后来的维护人员想当然的直接复制粘贴,最终问题爆发。
以后在定义类的时候要注意避免这种小聪明似的做法,而在维护别人的类和使用别人定义好的类时也要多研究下内部的代码,谨记。