Hibernate 提供兩種並發控制機制:悲觀鎖依賴資料庫鎖,樂觀鎖基於版本號應用層實現。選型需根據業務並發特性與效能要求權衡。
悲觀鎖
核心邏輯
假設並發衝突高發,讀取即加鎖(SELECT ... FOR UPDATE),阻塞其他事務讀寫,直至提交釋放。
JDBC 實現
SELECT * FROM account WHERE name = 'Erica' FOR UPDATE
Hibernate API
Query query = session.createQuery("FROM Account WHERE name = :name");
query.setParameter("name", "Erica");
query.setLockMode("account", LockMode.UPGRADE); // 關鍵:查詢前加鎖
Account account = (Account) query.uniqueResult();
鎖模式對照
| 模式 | 說明 | 資料庫層面 |
| NONE | 無鎖 | 無 |
| WRITE | Insert/Update 自動獲取 | 無 |
| READ | 讀取自動獲取 | 無 |
| UPGRADE | FOR UPDATE | 有 |
| UPGRADE_NOWAIT | FOR UPDATE NOWAIT (Oracle) | 有 |
效能考量
高併發下資料庫鎖競爭激烈,連線佔用久、吞吐率低。適用於:併發低、衝突嚴重、強一致性要求高的核心業務(如扣款、庫存扣減)。
樂觀鎖
核心邏輯
假設衝突稀少,讀取不加鎖,更新時比對版本號。版本匹配 → 更新成功並版本+1;不匹配 → 拋出 StaleObjectStateException,重試或回滾。
資料庫設計
ALTER TABLE account ADD COLUMN version INT DEFAULT 0;
實體類映射
@Entity
@Table(name = "account")
@org.hibernate.annotations.OptimisticLocking(type = OptimisticLockType.VERSION)
public class Account {
@Version
@Column(name = "version")
private int version;
// ... 其他欄位
}
或 XML 映射:<version name="version" column="VERSION" />
業務流程範例
- A 讀取餘額 1000,版本 5
- B 讀取餘額 1000,版本 5
- A 扣款 500 → 更新餘額 500,版本 6(5→6 成功)
- B 扣款 300 → 更新餘額 700,版本 6(5≠6 失敗,拋異常)
- B 捕獲異常 → 重讀最新資料 → 重新業務邏輯 → 再次提交
異常處理模式
try {
session.update(account);
tx.commit();
} catch (StaleObjectStateException e) {
// 策略:重讀、合併、提示用戶、自動重試
session.refresh(account); // 重新載入最新版本
// 重新應用業務邏輯...
}
外部系統風險警示
樂觀鎖僅保護本系統內更新。若外部系統(如銀行扣款回調、ERP 同步)繞過 Hibernate 直接更新資料庫,版本號不會遞增,導致髒寫。
對策:資料庫層面觸發器強制版本遞增、或關鍵表強制悲觀鎖、或引入分散式鎖協調。
選型決策表
| 維度 | 悲觀鎖 | 樂觀鎖 |
| 並發衝突頻率 | 高 | 低 |
| 持鎖時間 | 長(含業務邏輯) | 極短(僅更新瞬間) |
| 資料庫負載 | 高(鎖等待) | 低 |
| 適用場景 | 金融扣款、庫存扣減 | 用戶資料編輯、配置修改 |
| 外部系統干擾 | 免疫 | 脆弱 |
- 上一篇文章: Java 企業級資料權限設計架構實戰
- 下一篇文章: SQL 與 HQL 中邏輯運算子優先級詳解:NOT > AND > OR
Copyright ©
程式碼之家 All Rights Reserved