**********
做項目是否都有必要設置conn.setAutoCommit(false)?
public void updateCoffeeSales(HashMap<String, Integer> salesForWeek)
throws SQLException {
PreparedStatement updateSales = null;
PreparedStatement updateTotal = null;
String updateString =
"update " + dbName + ".COFFEES " +
"set SALES = ? where COF_NAME = ?";
String updateStatement =
"update " + dbName + ".COFFEES " +
"set TOTAL = TOTAL + ? " +
"where COF_NAME = ?";
try {
con.setAutoCommit(false);
updateSales = con.prepareStatement(updateString);
updateTotal = con.prepareStatement(updateStatement);
for (Map.Entry<String, Integer> e : salesForWeek.entrySet()) {
updateSales.setInt(1, e.getValue().intValue());
updateSales.setString(2, e.getKey());
updateSales.executeUpdate();
updateTotal.setInt(1, e.getValue().intValue());
updateTotal.setString(2, e.getKey());
updateTotal.executeUpdate();
con.commit();
}
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
if (con != null) {
try {
System.err.print("Transaction is being rolled back");
con.rollback();
} catch(SQLException excep) {
JDBCTutorialUtilities.printSQLException(excep);
}
}
} finally {
if (updateSales != null) {
updateSales.close();
}
if (updateTotal != null) {
updateTotal.close();
}
con.setAutoCommit(true);
}
}
http://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html
畫紅線的代碼是否有必要?
csdn帖子:
一般來講,大家對數據庫中的表單,主要是增、刪、改、查 這四個操作,如果你的程序當中,遇到一次業務邏輯需要兩次或兩次以上的對相同數據表的增刪改操作,那么,為了數據的一致性,(或者具體說,在你的一次業務邏輯的處理過程中,其他(線程或程序或業務處理)的對相同數據的相同兩次查詢所得結果相同。)我們會利用數據庫的事務操作,將一次業務邏輯所包含的所有操作封裝在一個事務里,一次將多個操作進行提交。
而conn的setAutoCommit方法,是指,在事務當中,是否執行一條語句就自動提交一次。
這樣的話,如果,你想在一個事務里進行多個操作。就必然將setAutoCommit的參數設置成false,在多個操作的最后調用conn.commit()方法,進行手動提交。
有時候是很必要的,特別是純JDBC的事務控制,就更需要了,舉個例子來說吧,你要刪除某個部門,而該部門下又有多個員工,這時候,你就得先刪除該部門下的所有員工,這里,你就得把兩個操作(刪除員工和刪除部門)放在同一個業務中了,而這種情況下是很危險的,你可能成功刪除了該部門下的所有員工而刪除該部門的時候又遇到了異常即沒有成功刪除。這時候,如果你原先沒有把事務提交方式改為手工提交,而之前刪除的員工的事務又已經自動提交了,就無法進行數據回滾,員工的數據就找不回了,這時,你會45度仰望天空,感嘆一聲:“啊,悲劇終於發生了”。
誤用Connection.setAutoCommit導致的數據庫死鎖問題。
系統在發布到用戶的的服務器了,運行一段時間偶爾出現某些功能不能正常運行,甚至不能自動恢復,嚴重導致服務器當機,表現為服務器不響應用戶的請求,數據庫有大量的鎖。在服務器重啟后才能恢復正常。今天通遍的查看了一下代碼,初步分析了原因,記錄了下來,跟大家交流交流。
先看下面一段有問題的代碼:
1 Connection con = null;
2 try{
3 con = getConnection();
4 con.setAutoCommit(false);
/*
5 * update USER set name=’winson’ where id=’000001’;
*/
6 con.commit();
7 }finally{
8 if(con!=null){
9 try {
10 con.close();
11 } catch (SQLException e) {
12 e.printStackTrace();
13 }
}
}
分析:問題就出現在第4行,寫代碼的人把數據庫連接con 設置成非自動提交,但沒有在執行出現異常的時候進行回滾。如果在執行第5行的時候出現異常,con既沒有提交也沒有回滾,表USER就會被鎖住(如果oracle數據庫就是行鎖),而這個鎖卻沒有機會釋放。有人會質疑,在執行con.close()的時候不會釋放鎖嗎?因為如果應用服務器使用了數據庫連接池,連接不會被斷開。
附:在oracle上查看鎖的方法:select * from v$lock_object或者select * from v$lock.
JDBC的api文檔是這樣對setAutoCommit方法解釋的:
Sets the connection's auto-commit mode to enableAutoCommit.
Newly created Connection objects are in auto-commit mode by default, which means that individual SQL statements are committed automatically when the statement is completed. To be able to group SQL statements into transactions and commit them or roll them back as a unit, auto-commit must be disabled by calling the method setAutoCommit with false as its argument. When auto-commit is disabled, the user must call either the commit or rollback method explicitly to end a transaction.(一定不能大意哦,如果設置成非自動提交,在最后一定要調用commit或者rollback方法)
The commit occurs when the statement completes or the next execute occurs, whichever comes first. In the case of statements returning a ResultSet object, the statement completes when the last row of the result set has been retrieved or the ResultSet object has been closed. In advanced cases, a single statement may return multiple results as well as output parameter values. In this case, the commit may occur when all results and output parameter values have been retrieved, or the commit may occur after each result is retrieved.
參考正確的寫法應該是:
Connection con = null;
try{
con = getConnection();
con.setAutoCommit(false);
/*
* do what you want here.
*/
con.commit();
}catch(Throwable e){
if(con!=null){
try {
con.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
throw new RuntimeException(e);
}finally{
if(con!=null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
這種疏忽很容易出現,但又導致非常嚴重的運行問題。所以在這里作個提醒,以后在處理外部資源的時候一定要格外小心。今天還發現代碼中一些地方濫用synchronized關鍵字,導致系統性能受到很大的影響,處理不當跟前面提到問題一起出現,那系統就是時候over了。
另外,如果不是自己來處理事務,可能在用hibernate或者ejb等,都一定要記住在處理完之后要提交或者回滾哦。