為什麼需要交易
很多時候,在使用數據庫時,會出現需要執行許多不同操作的情況,但它們只有放在一起才有意義。
例如,我們正在編寫的銀行軟件應該做三件事:
- 從客戶賬戶中提取資金
- 將錢添加到收款人的帳戶
- 將投寄資料記錄於「投寄紀錄」
如果在執行這些操作中的任何一個期間發生錯誤,則其他兩個操作也必須取消。不可能從客戶那裡註銷錢而不把它加到收款人身上?好吧,還是添加到收件人,但不從客戶端註銷?
因此,這種將不同操作合而為一的邏輯組合稱為事務。換句話說,事務是一組必須一起執行的操作。如果任何操作失敗或執行出錯,則必須取消所有其他操作。
一個事務通常有三種狀態:
- 初始狀態——執行一組動作之前的系統狀態
- 成功狀態——動作組完成後的狀態
- 失敗狀態 - 出了點問題
在這種情況下,通常有三個命令:
- begin/start - 在邏輯動作組開始之前執行
- commit - 在事務操作組之後執行
- 回滾- 啟動將系統從失敗狀態返回到初始狀態的過程
它是這樣工作的。
首先您需要打開一個事務——調用begin()或start()方法。調用此方法表示出現問題時我們將嘗試返回的系統狀態。
然後執行所有操作,這些操作組合成一個邏輯組 - 事務。
然後調用commit()方法。它的調用標誌著一組邏輯動作的結束,通常也開始將這些動作付諸實踐的過程。
回想一下我們在 FileWriter 中是如何寫的:首先,我們寫的所有內容都存儲在內存中,然後當調用flush()方法時,將內存中緩衝區中的所有數據寫入磁盤。這個flush()是事務提交。
那麼,如果在事務運行過程中發生了錯誤,那麼就需要發起返回起始狀態的過程。這個過程稱為rollback(),同名方法通常負責它。
粗略地說,有兩種方式可以完成一筆交易:
- COMMIT - 我們確認所做的所有更改
- ROLLBACK - 回滾所有所做的更改
JDBC 中的事務
幾乎每個 DBMS 都可以處理事務。所以JDBC也有這種情況的支持。一切都非常簡單地實現。
首先,對Statement 對象的execute()方法的每次調用都在單獨的事務中執行。為此,Connection 有一個AutoCommit參數。如果設置為true,則每次調用execute()方法後都會調用commit()。
其次,如果你想在一個事務中執行多個命令,那麼你可以這樣做:
- 禁用自動提交
- 調用我們的命令
- 顯式調用commit()方法
它看起來很簡單:
connection.setAutoCommit(false);
Statement statement = connection.createStatement();
int rowsCount1 = statement.executeUpdate("UPDATE employee SET salary = salary+1000");
int rowsCount2 = statement.executeUpdate("UPDATE employee SET salary = salary+1000");
int rowsCount3 = statement.executeUpdate("UPDATE employee SET salary = salary+1000");
connection.commit();
如果commit()方法運行時服務器發生錯誤,則 SQL 服務器將取消所有這三個操作。
但是在某些情況下,錯誤仍然發生在客戶端,而我們從未進行過commit()方法調用:
connection.setAutoCommit(false);
Statement statement = connection.createStatement();
int rowsCount1 = statement.executeUpdate("UPDATE employee SET salary = salary+1000");
int rowsCount2 = statement.executeUpdate("UPDATE employee SET salary = salary+1000");
int rowsCount3 = statement.executeUpdate("UPDATE multiple typos will result in an exception");
connection.commit();
如果在執行一次executeUpdate()時發生錯誤,則不會調用commit()方法。要回滾所有採取的操作,您需要調用rollback()方法。它通常看起來像這樣:
try{
connection.setAutoCommit(false);
Statement statement = connection.createStatement();
int rowsCount1 = statement.executeUpdate("UPDATE employee SET salary = salary+1000");
int rowsCount2 = statement.executeUpdate("UPDATE employee SET salary = salary+1000");
int rowsCount3 = statement.executeUpdate("UPDATE multiple typos will result in an exception");
connection.commit();
}
catch (Exception e) {
connection.rollback();
}
保存點
隨著 JDBC 3.0 的出現,可以更有效地處理事務回滾。現在可以設置save points-save points,調用rollback()操作時,回滾到特定的save point。
為了保存,您需要創建一個保存點,這是通過命令完成的:
Savepoint save = connection.setSavepoint();
恢復到保存點是通過以下命令完成的:
connection.rollback(save);
讓我們嘗試在有問題的命令之前添加一個保存點:
try{
connection.setAutoCommit(false);
Statement statement = connection.createStatement();
int rowsCount1 = statement.executeUpdate("UPDATE employee SET salary = salary+1000");
int rowsCount2 = statement.executeUpdate("UPDATE employee SET salary = salary+1000");
Savepoint save = connection.setSavepoint();
try{
int rowsCount3 = statement.executeUpdate("UPDATE multiple typos will result in an exception");
}
catch (Exception e) {
connection.rollback(save);
}
connection.commit();
}
catch (Exception e) {
connection.rollback();
}
我們通過在調用有問題的方法之前添加一個保存點來組織嵌套事務,並通過調用rollback(save)方法返回到保存的狀態。
是的,它與遊戲中的保存/加載非常相似。
GO TO FULL VERSION