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