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