JDBC異常處理和資源釋放問題
之前我們在介紹JDBC載入註冊驅動的時候說過有三種方式,是哪三種方式呢?我們再來看看
Class.forName("com.mysql.jdbc.Driver");
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
System.setProperty("jdbc.drivers", "com.mysql.jdbc.Driver");
以上三種方式都可以實現JDBC的驅動載入註冊,這裡我們推薦使用Class.forName(“com.mysql.jdbc.Driver”);另外DriverManager.registerDriver(new com.mysql.jdbc.Driver());其實可以直接寫成new com.mysql.jdbc.Driver();這裡我們瞭解就好,大可不必深究,除非有必要!
今天我們來說說JDBC連線資料庫的異常的正確處理,還記得之前我們是怎麼處理的嗎?沒錯,我們直接給丟擲了,程式碼是這個樣子滴!
@Test
public void ddlAndExceptionTest() throws Exception{
String sql = "CREATE TABLE `t_student` (`id` bigint(10) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
//載入註冊驅動
Class.forName("com.mysql.jdbc.Driver" );
//獲取連線物件
Connection connection = DriverManager.getConnection("jdbc:mysql:///jdbcdemo", "root", "123456");
//建立獲取語句物件
Statement st = connection.createStatement();
//執行SQL語句
st.executeUpdate(sql);
//釋放資源
st.close();
connection.close();
}
我們這裡直接throws Exception了,其實這樣是不妥的,那正確的異常處理方式是什麼呢?在此之前我們應該知道處理異常我們都用try catch的,有時候也配合finally一起使用,在處理JDBC連線資料庫的異常時我們需要著重瞭解的還有資源釋放的問題,就是那個xxx.close,下面我們一步步結合程式碼來說
首先我們看需要處理異常的程式碼
諸如此類的程式碼異常我們都將它使用try catch包裹
//載入註冊驅動
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
然後將catch中的異常型別改為Exception,然後繼續寫程式碼,完成JDBC連線資料庫的後續步驟,程式碼如
try {
//載入註冊驅動
Class.forName("com.mysql.jdbc.Driver");
//獲取連線物件
Connection connection = DriverManager.getConnection("jdbc:mysql:///jdbcdemo","root","123456");
//建立或者獲取語句物件
Statement statement = connection.createStatement();
//執行sql語句
statement.executeUpdate(sql);
//釋放資源
statement.close();
connection.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
到這裡我們就已經將異常做了相應的處理,但是這裡面還有個很大的問題那就是資源釋放的問題,也就是說目前釋放資源的程式碼放在那裡是不妥的,如果之前程式碼出現個什麼意外,那麼資源就無法釋放了,可能有人說了,為什麼這個資源必須要釋放呢?
那麼我們就來說道說道這個資源為什麼必須釋放呢?
我們先來看這個Connection,這傢伙是資料庫的連線物件,你可要知道在JDBC中Connection這個資源是非常稀有的,使用之後必須馬上釋放,我記得在哪看過關於這塊釋放資源的一個原則好像是“晚點用,早點放”,嗯,大概就是這個意思,總之在使用的過程中只有用到的時候才去建立這個物件,使用之後也要記得立馬釋放。
因此,為了保證資源的必須釋放,我們該怎麼做呢?沒錯,我們可以將釋放資源的程式碼放到finally裡,這樣就可以保證無論之前的程式碼怎樣,釋放資源的程式碼必定執行。
這樣程式碼就變成了現在這個樣子了
@Test
public void testException(){
String sql = "CREATE TABLE `t_one` (`id` bigint(10) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
Statement statement = null;
Connection connection = null;
try {
//載入註冊驅動
Class.forName("com.mysql.jdbc.Driver");
//獲取連線物件
connection = DriverManager.getConnection("jdbc:mysql:///jdbcdemo","root","123456");
//建立或者獲取語句物件
statement = connection.createStatement();
//執行sql語句
statement.executeUpdate(sql);
//釋放資源
statement.close();
connection.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
//釋放資源
try {
statement.close();
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
到了這一步,你看看是不是就可以了呢?其實不然,這裡面還是不行,哪裡不行呢?我們來看,假如在connection沒有來得及賦值的時候出現了意外,這樣就會直接執行finally裡面的程式碼,這樣就會導致一個空物件呼叫close,就會引起新的異常導致程式崩潰,對於statement依然如此,因此,我們需要進行判空!
因此finally裡面釋放資源的程式碼應該這樣寫
finally {
//釋放資源
if (statement!=null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (connection!=null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
因此正確完整的程式碼如下
//正確處理JDBC的異常
@Test
public void testHandleException(){
Connection con = null;
Statement st = null;
String sql = "CREATE TABLE `t_new1` (`id` bigint(10) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
//異常標準程式碼格式結構
try{
//這裡存放可能出現異常的程式碼
//載入註冊驅動
Class.forName("com.mysql.jdbc.Driver");
//獲取連線物件
con = DriverManager.getConnection("jdbc:mysql:///jdbcdemo","root","123456");
//獲取連線語句
st = con.createStatement();
//執行sql語句
st.executeUpdate(sql);
}
catch (Exception e) {
e.printStackTrace();
}finally {
//釋放資源、
if (st!=null) {
try {
st.close();
} catch (Exception e2) {
// TODO: handle exception
}finally {
if (con!=null) {
try {
con.close();
} catch (Exception e3) {
// TODO: handle exception
}
}
}
}
}
}
歡迎關注微信公眾號:一個自學的程式設計師