[JDBC]你真的会正确关闭connection吗?

         Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		try {
			conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/test","root","fendou");
			stmt = conn.prepareStatement("select 1 from dual");
			rs = stmt.executeQuery();
			while(rs.next()){
				System.out.println("OK");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
				try {
					if(rs != null){
						rs.close();
					}
					if(stmt != null){
						stmt.close();
					}
					if(conn != null){
						conn.close();
					}
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}		

  上面是一段很常见的jdbc代码.通常,我们都是在finally里释放资源,经常可以看到有人或者为了美观,或者为了省事,将rs.close(),stmt.close(),conn.close()放到同一个try,catch块中.事实上,这样子关闭是不够严谨是.如果rs.close()或者stmt.close()执行的时候抛出了异常,那么就不会执行conn.close(),也就没法正确关闭connection了.

  为了保证connection可以正常关闭,我们稍微调整一下代码.如下:

finally{
				try {
					if(rs != null){
						rs.close();
					}
				} catch (SQLException e) {
					//handle the exception
				}
				try{
					if(stmt != null){
						stmt.close();
					}
				}catch(SQLException e){
					//handle the exception
				}
				
				try{
					if(conn!= null){
						conn.close();
					}
				}catch(SQLException e){
					//handle the exception
				}
				
					

  这样即使出了异常,也可以保证代码向下执行.这种情况下,通常我们会在catch块中通常忽略掉异常,不去做处理,或者做简单的打印一下,但是不能将异常转化成运行时异常抛出。因为一旦在catch块中抛出异常,程序就无法向下执行了。

  当然,最正确的写法应该是这样:  

try {
				if(rs != null){
					rs.close();
				}
			}catch(SQLException e){
				// do something
			} finally{
				try{
					if(stmt != null){
						stmt.close();
					}
				}catch(Exception e){
					// do something
				}finally{
					if(conn != null){
						try {
							conn.close();
						} catch (SQLException e) {
							// do something
						}
					}
				}
			}

  这样的写法虽然正确,但是看起来十分的丑陋,于是采取了一个折中的办法.在方法上声明throws SQLException,方法体中这样关闭,代码相对简洁一些.

try {
					if(rs != null){
						rs.close();//(1)
					}
				} finally{
					try{
						if(stmt != null){
							stmt.close();//(2)
						}
					}finally{
						if(conn != null){
							conn.close();//(3)
						}
					}
				}

  这时候我会有这样一个疑问,如果(1)(2)(3)处都抛出了异常,那么方法会抛出哪儿个异常.事实证明,最后产生是异常会被抛出.前面的两个异常会被丢弃掉.

  一般我会定义两个方法来专门处理关闭,根据不能的需求,一个会抛出SQLException,一个会忽略掉异常(ignore).相对来说,这样的代码最简洁,省去了重复的try,catch.

        // close(conn,stmt,rs)
	// close(conn,stmt,null)
	// close(conn,null,null)
	public static void close(Connection conn, PreparedStatement stmt,
			ResultSet rs) throws SQLException {
		try {
			if(rs != null){
				rs.close();
			}
		} finally{
			try{
				if(stmt != null){
					stmt.close();
				}
			}finally{
				if(conn != null){
					conn.close();
				}
			}
		}
	}
	
	public static void closeQuietly(Connection conn, PreparedStatement stmt,
			ResultSet rs){
		try{
			 close(conn, stmt,rs);
		}catch(SQLException e){
			//quietly
		}
	}

  

  很多人在关闭的时候喜欢这么写:

//略去stmt和rs
connection.close(); 
connection = null;

  这里讨论下connection = null这句是否有必要.有人说connection =null有助于垃圾回收.

  个人认为connection  = null是没有必要的.

  从原理上来说,connection对象的回收,与它的状态是否是isClosed没有关系,而是取决于这个对象是否被引用.换句话说,即使connection没有close,也是可以被回收的.close()方法的作用是通知数据库端及时的释放资源.

  经过下面程序的验证,即使不写connection=null,connection也可以很好的被垃圾回收.没有看出connection=null对垃圾回收有什么积极的影响;

  另外从打印结果来看,垃圾收集是并行的.  

package me.qiu.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class CloseDemo {

	public static void main(String[] args){
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		for (int i = 0; i < 100; i++) {
			jdbc();
		}
	}



	private static void jdbc() {
		MyConnection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		int b = 0;
		try {
			conn = new MyConnection(DriverManager.getConnection("jdbc:mysql://127.0.0.1/test","root","fendou"));
			stmt = conn.prepareStatement("select 1 from dual");
			rs = stmt.executeQuery();
			while(rs.next()){
				b = rs.getInt(1);
				System.out.println(b);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			closeQuietly(conn, stmt, rs);
		}
	}

	
	
	// close(conn,stmt,rs)
	// close(conn,stmt,null)
	// close(conn,null,null)
	public static void close(MyConnection conn, PreparedStatement stmt,
			ResultSet rs) throws SQLException {
		try {
			if(rs != null){
				rs.close();
				rs = null;
			}
		} finally{
			try{
				if(stmt != null){
					stmt.close();
					stmt = null;
				}
			}finally{
				if(conn != null){
					conn.close();
//					conn = null;
				}
			}
		}
	}
	
	public static void closeQuietly(MyConnection conn, PreparedStatement stmt,
			ResultSet rs){
		try{
			 close(conn, stmt,rs);
		}catch(SQLException e){
			//quietly
		}
	}
}


class MyConnection{
	Connection conn;
	MyConnection(Connection conn){
		this.conn = conn;
	}
	
	
	public PreparedStatement prepareStatement(String sql) throws SQLException {
		return conn.prepareStatement(sql);
	}


	public void close() throws SQLException{
		conn.close();
	}
	
	@Override
	protected void finalize() throws Throwable {
		System.out.println("gc ...");
	}
}

  

  

  

 

posted on 2013-07-18 01:04  LoveEyes  阅读(9904)  评论(2编辑  收藏  举报

导航