我們可以將一組語句構建成一個事務(transaction)。當所有語句都順利執行之后,事務可以提交(commit)。否則,如果其中某個語句遇到錯誤,那么事務將被回滾,就好像沒有任何語句被執行過一樣。
將多個語句組合成事務的主要原因是為了確保數據庫完整性(database integrity)。例如,假設我們需要將錢從一個銀行賬號轉移到另一個賬號。此時,一個非常重要的問題就是我們必須同時將錢從一個賬號取出並且存入另一個賬號。如果在將錢存入其他賬號之前系統發生崩潰,那么我們必須撤銷取款操作。
如果將更新語句組合成一個事務,那么事務要么成功地執行所有操作並提交,要么期間某個位置發生失敗。在這種情況下,可以執行回滾(rollback)操作,則數據庫將自動撤銷上次提交事務以來的所有更新操作產生的影響。
默認情況下,數據庫連接處於自動提交模式(autocommit mode)。每個SQL語句一旦被執行便被提交給數據庫。一旦命令被提交,就無法對它進行回滾操作。在使用事務時,需要關閉這個默認值:
conn.setAutoCommit(false);
現在可以使用通常的方法創建一個語句對象:
Statement stat = conn.createStatement();
然后任意多次調用executeUpdate方法:
stat.executeUpdate(command1);
stat.executeUpdate(command2);
stat.executeUpdate(command3);
...
如果執行了所有命令之后沒有出錯,則調用commit方法:
conn.commit();
如果出現錯誤,則調用:
conn.rollback();
此時,程序將自動撤銷自上次提交以來的所有語句。當事務被SQLException異常中斷時,典型的方法就是發起回滾操作。
1. 保存點
在使用某些驅動程序時,使用保存點(save point)可以更細粒度地控制回滾操作。創建一個保存點意味着稍候只需返回到這個點,而非事務的開頭。例如,
Statement stat = conn.createStatement(); //開啟一個事務;rollback()返回這里 stat.executeUpdate(command1); Savepoint svpt = conn.setSavepoint(); //設置保存點;rollback(svpt)返回到這里 stat.executeUpdate(command2); if(...) conn.rollback(svpt); //撤銷command2產生的影響 ... conn.commit();
當不再需要保存點時,必須釋放它:
conn.releaseSavepoint(svpt);
2. 批量更新
假設有一個程序需要執行許多INSERT語句,以便將數據填入數據庫表中,此時可以使用批量更新的方法來提高程序性能。在使用批量更新(batch update)時,一個語句序列作為一批操作將同時被收集和提交。
注意:使用DatabaseMetaData接口中的supportsBatchUpdates方法可以獲知數據庫是否支持這種特性。
處於同一批中的語句可以是INSERT、UPDATE和DELETE等操作,也可以是數據庫定義語句,如CREATE TABLE和DROP TABLE。但是,在批量處理中添加SELECT語句會拋出異常(從概念上講,批量處理中的SELECT語句沒有意義,因為它會返回結果集,而不更新數據庫)。
為了執行批量處理,首先必須使用通常的辦法創建一個Statement對象:
Statement stat = conn.createStatement();
現在應該調用addBatch方法,而非executeUpdate方法:
String command = "CREATE TABLE..." stat.addBatch(command); while(...){ command = "INSERT INTO ... VALUES("+...+")"; stat.addBatch(command); }
最后,提交整個批量更新語句:
int[] counts = stat.executeBatch();
調用executeBatch方法將為所有已提交的語句返回一個記錄數的數組。
為了在批量模式下正確地處理錯誤,必須將批量執行的操作視為單個事務。如果批量更新在執行過程中失敗,那么必須將它回滾到批量操作開始之前的狀態。
首先,關閉自動提交模式,然后收集批量操作,執行並提交該操作,最后恢復最初的自動提交模式:
boolean autoCommit = conn.getAutoCommit(); conn.setAutoCommit(false); Statement stat = conn.getStatement(); ... //keep calling stat.addBatch(...); ... stat.executeBatch(); conn.commit(); conn.setAutoCommit(autoCommit);