在面試中,我常問這樣一個問題:假設有一個全局變量,在一個事務中修改了其值,隨後該事務因故回滾,這個變量的值會恢復到修改前嗎?
答案是否定的。事務僅能管理其管轄範圍內的資源(即事務資源),典型包括資料庫連接、JMS 隊列等。ACID 特性(原子性、一致性、隔離性、持久性)也僅針對這些受管資源生效。全局變量本質上是記憶體中的一塊存儲空間,記憶體數據並不具備持久性,自然不在事務的管理範圍內,因此不會隨事務回滾而還原。
在手動控制事務的場景中,通常採用 try-catch-finally 模式:
Connection cn = ...;
cn.setAutoCommit(false);
Statement stmt = cn.createStatement();
try {
stmt.executeUpdate("update Order...");
cn.commit();
} catch (Exception e) {
cn.rollback(); // 異常發生時回滾
} finally {
stmt.close();
cn.close();
}
容器管理事務下,開發者僅需聲明方法需要事務控制。容器會在方法進入時啟動事務,在方法正常返回前提交。但異常處理有默認規則:僅當拋出 RuntimeException 或標註 @ApplicationException 的異常時才會回滾,其他檢查型異常(Checked Exception)預設會提交事務,導致「部分提交」風險。
為確保方法內所有操作要么全提交要么全回滾,常見做法是捕獲異常並包裝為 EJBException(繼承自 RuntimeException)重新拋出:
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void updateOrder(Order order) {
try {
// 業務邏輯
} catch (Exception e) {
throw new EJBException(e); // 強制觸發回滾
}
}
此外,setRollbackOnly() 僅標記事務為「僅回滾」,不拋出異常,容器在方法結束時檢查標記並回滾。
Spring 允許顯式指定觸發回滾的異常類型:
@Transactional(rollbackFor = {Exception.class})
public void updateOrder(Order order) {
// 所有 Exception 及其子類均觸發回滾
}
最佳實踐:不要依賴默認行為,明確聲明 rollbackFor 與 noRollbackFor,讓事務邊界語義清晰可控。
某專案從 JDBC 遷移至 Spring + Hibernate,採用聲明式事務後遭遇性能瓶頸。原因在於聲明式事務的邊界默認綁定到整個方法,若方法較長但僅有少數數據庫操作需事務保護,會導致事務過長,長期佔用連接資源,阻塞其他線程。
解決思路:
PlatformTransactionManager 手動控制事務邊界(犧牲「非侵入性」換取精準控制)。readOnly=true:純查詢方法標註唯讀,可減少鎖競爭,提升並發性能。分布式事務不僅指跨資料庫,還包括同一進程中同時操作本地資料庫與 JMS 隊列等異構資源。EJB 容器(如 JBoss)通過 JTA UserTransaction 統一管理所有事務資源。
Spring 單機環境下不直接支持 JTA,需引入 JOTM 等獨立事務管理器。專案從 JBoss 遷移至 Jetty 時,若忽略分布式事務配置,極易導致數據不一致。遷移時務必:
UserTransaction 用法改為 Spring 的 JtaTransactionManager。總結:事務管理的核心在於明確資源邊界、精準控制回滾條件、合理縮小事務範圍、謹慎處理分布式場景。唯有深入理解底層機制,才能避開生產環境中的隱性坑洞。