日期:2014-05-16  浏览次数:20497 次

关于java中关闭数据库链接的正确写法

转自:http://hi.baidu.com/zzcc_8/blog/item/ddbcc42ed767d25b4fc226bd.html
作者:爱题巴.爱技术.小川哥

近期看到在某贴里有一个小程序,内涉到连库的技术,所以发个帖子讨论一下。
??? 首先,连库语句和关闭连接的部分都应该单独提取出来放在一个类里,有可能的话,连库语句还能以输入或从文件中读取的形式做出来,这是因为连库的语句有可能会出现变化,比如:今天连mysql,明天可能因为升级或其他原因换成了oracle,如果你有50个类都涉及到连库那你就要改50个地方,而把连库语句放在一个类中,由其他类去调用它,那么改的时候则只需要改这一个地方,而对关闭连接语句的改动情况可能不会很多但是,由于连一次库就需要有一次关闭连接,连50次库就需要写50遍几乎一模一样的关闭连接语句,写代码即麻烦又不够漂亮。像这样以让结果不发生改变为前提,通过修改代码结构来减少以后修改的麻烦,让代码写的更漂亮的技术就是“重构”,关于重构问题参与过项目的人其实都很清楚其重要性,我在这里就不多说了,为那些还在受到大学里连命名都用a,b,c的无良教育的兄弟们推荐“重构--改善既有代码的设计”这本书,马丁大师的杰作,建议这本书要买下来,而不是去图书馆借。
??? 由于我重点想说的是关闭连接,所以对连库方面就说这么多,下面重点说说关闭连接的写法,以下是我看到的那个帖子里写的关闭连接方法(命名可能不同),
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
conn = XXX;
ps = conn.XXX;
rs = ps.XXX;
??? 下面是重点
rs.close();
ps.close();
conn.close();
??? 看到上面的关闭语句了吗?只有3行,但看到这里的人不知道有多少看出这3行中的问题?至少我认为,那些正在接受大学过时教育的学生们都不会看出这里有什么样的问题。那么下面我们就分析一下:

??? 首先conn,ps,rs的初值为null,就是说还没有连接的情况,那么如果在你的程序中,这3个中有1个,2个甚至3个都没有用到或在之前被置空怎么半?比如你有可能没有用到rs,那么就会出现这种情况:
rs = null;
rs.close();
??? 很明显,没有打开的东西为什么还要关闭?所以只在这个问题上正确的写法如下:
if(ps != null){
ps.close();
ps = null;
}

??? 上面的写法就比最开始那个3行关闭要好的多,但这个的写法依然存在问题,每个人都知道在写数据库的操作时会用到下面这个语法:
try{
...
}catch(SQLException e){
e.printStackTrace();
}finally{
...
}
??? 每个人还都知道这是为了应付中途出现数据库错误而调用的一段错误处理,然后try剩余的部分会终止并从finally开始运行。所以,关闭连接的语句的问题就出现在了这里,我知道肯定有人把关闭语句写在try里因而省掉了finally的部分,但这么做的话,中途出现错误就会导致关闭语句没有被执行,所以关闭语句必须要写在finally里,另外,万一rs,ps中有一个出错,那么conn怎么办?比如说:rs.close()出错,ps和conn的close怎么办?这点也必须考虑进去,因而关闭语句的正确的写法应该是下面这个样子:

try{
??? if(result != null){
??????? result.close();
??????? result = null;
??? }?
}catch(SQLException e){
??? e.printStackTrace();
}finally{
??? try{
??????? if(ps != null){
??????????? ps.close();
???? ps = null;
?? }
??? }catch(SQLException e){
??????? e.printStackTrace();
??? }finally{
??????? try{
???? if(conn != null){
?? conn.close();
?? conn = null;
???? }
}catch(SQLException e){
???? e.printStackTrace();
}finally{
???? return "数据库连接已关闭";
}
??? }
}

??? 然后把上面那段代码放在下面这个总的finally中就行了,由于还要对格式我就不写了

try{
??? Connection conn = null;
??? PreparedStatement ps = null;
??? ResultSet rs = null;
??? conn = XXX;
??? ps = conn.XXX;
??? ...
??? rs = ps.XXX;
}catch(SQLException e){
e.printStackTrace();
}finally{
??? //这里放那段关闭代码
}

??? 不要觉得这段关闭代码多余,如果你去应聘公司,面试官让你写出关闭连接的代码,写成3行的那种和写成最后这种明显是不同的,虽然相比多出很多代码,不过你把这些关闭代码提取到一个类中去,通过参数传递conn,ps,rs,只需要写这一遍就可以用一辈子了。如果谁还知道我这种写法还存在什么漏洞也请指教。

??? 最后还是忍不住再说两句,根据重构法则,要尽量消除重复的代码,所以如果conn,ps和rs在你的一个类中出现了不只一次(比如2个方法都用到了它们),则可以考虑把他们变成类的私有属性提出来,如下:
private Connection conn = null;
private PreparedStatement ps = null;
private ResultSet rs = null;

??? 另外还有一点,上次看到的那个小程序中把存取函数(set和get方法)单独提了出来,这种做法很不错,但是如果不需要外界调用的话,则可以不用写,因为多加一个存取函数就相当于对外多开放了一个接口,有的时候你多写一个存取函数无所谓,但有时候则会出现安全性的问题哦。

?