[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 ...");
	}
}

  

  

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM