在 Java 中使用 SQLite 資料庫時,假設我將自動提交設定為 false。發生SQLException時是否有必要呼叫rollback()方法?或者我可以簡單地忽略呼叫它并且事務將自動回滾(我在事務期間所做的所有更改都將自動撤消)?
uj5u.com熱心網友回復:
快速回答:你問的事實可能意味著你做錯了。但是,如果您必須知道:是的,您需要顯式回滾。
引擎蓋下發生了什么
在 JDBC 級別(如果您使用 JOOQ、JDBI、Hibernate 或類似的東西,那通常是構建在 JDBC 之上的庫),您有一個Connection實體。您可能已經通過DriverManager.getConnection(...)- 或者連接池為您獲得了它,但有些事情確實發生了。
該連接可以在事務的中間(自動提交模式僅意味著該連接假定您打算commit()在您關心在該連接的背景關系中運行的每個 SQL 陳述句之后撰寫一個附加內容,這就是自動提交所做的一切,但是,顯然,如果打開,您可能處于“干凈”狀態,也就是說,該連接處理的最后一個命令是COMMIT或ROLLBACK)。
如果它在事務的中間并且您關閉了連接,則ROLLBACK它是隱式的。
連接必須做出選擇,它不能保持存在,因此,它提交或回滾。該規范保證它不只是為了你的樂趣而承諾,因此,它會回滾。
然后問題歸結為您的特定設定。具體來說,這是危險的:
try (Connection con = ...) {
con.setAutoCommit(false);
try {
try (var s = con.createStatement()) {
s.execute("DROP TABLE foobar");
}
} catch (SQLException ignore) {
// ignoring an exception usually bad idea. But for sake of example..
}
// A second statement on the same connection...
try (var s = con.createStatement()) {
s.execute("DROP TABLE quux");
}
}
就規范而言,JDBC 驅動程式可以自由地拋出 SQLException,類似于“連接已中止;您必須先顯式回滾,然后才能在第二條陳述句中再次使用它。
但是,上面的代碼很糟糕。對于這種代碼,您根本無法使用事務隔離級別 SERIALIZABLE(一旦您獲得的用戶數量超過少數,應用程式將崩潰并在重試例外行列中燒毀),并且它要么在做一些無用的事情(重用當您使用連接池時,多個事務的 1 個連接),或者正在嚴重解決問題(問題:為每個事務使用新連接是昂貴的)。
1 筆交易,1 次連接
上述危險的唯一原因是因為我們在與連接物件關聯的單個 try-block 中執行了兩件不相關的事情(即:2 個事務)。我們正在重新使用連接。這是一個壞主意:連接有與之關聯的包袱:已設定的屬性,是的,處于“中止”狀態(需要顯式回滾在連接愿意執行任何其他 SQL 之前)。只需關閉連接并獲得一個新連接,您就可以拋棄所有這些包袱。這種包袱會導致單元測驗不容易捕捉到的錯誤,也就是一旦觸發就會花費大量金錢/眼球/善意/時間來修復的錯誤。客觀地說,如果它避免了一個 100 倍難以捕捉的 bug,那么你必須更喜歡 99 個易于捕捉的 bug,而這是屬于后一類的那些 bug 之一。
連接很貴?什么?
這樣做有一個問題:只需為單個事務使用一個連接,然后將其交回,這樣就無需回滾,因為如果您close()這樣做,連接將自動執行此操作:獲取連接非常耗費資源。
因此,人們傾向于/可能應該使用連接池來避免這種成本。也不要在這里寫你自己的;使用HikariCP或類似的東西。這些工具為您匯集連接:您無需呼叫DriverManager.getConnection,而是向 HikariCP 索要一個,并在完成后將連接交還給 HikariCP。Hikari 將負責為您重置它,包括在連接處于事務中途時回滾,并處理任何其他每個連接的設定,使其恢復到已知狀態。
常見的 DB 互動模型本質上是這種“流程”:
someDbAccessorObject.act(db -> {
// do a single transaction here
});
就是這樣。這段代碼在底層做了各種各樣的事情:
- 使用連接池。
- 以正確的方式建立連接,主要包括將 auto-commit 設定為 false,以及設定正確的事務隔離級別。
- 如果沒有發生例外,將在 lambda 塊的末尾提交。無論哪種情況,都將連接交還給池。
- 將捕獲 SQLExceptions 并分析它們是否是重試例外。如果是,nagle 的演算法或其他一些隨機指數退避并重新運行 lambda 塊(這就是重試例外的含義)。
- 負責將“獲取”連接的代碼(例如,確定要使用的正確 JDBC url)放在一個地方,因此 db 配置的更改不需要在您的代碼庫中進行全域搜索/替換狂歡。
在該模型中,您遇到問題的情況很少見,因為您最終會陷入 '1 交易?1 個連接!模型。通常這是昂貴的(創建連接比像往常一樣回滾/提交然后繼續在同一個連接物件上進行新事務要昂貴得多),但是一旦使用池化器,它就歸結為同樣的事情。
換句話說:除非您自己撰寫連接池程式,否則正確撰寫的資料庫代碼不應該有您的問題,在這種情況下,答案肯定是:顯式回滾。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/412427.html
標籤:
