首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Hibernate 鎖機制與查詢方式深度解析:悲觀鎖、樂觀鎖、HQL/QBC/SQL 對比

     編輯:Java 相關知識

為什麼需要鎖?事務隔離性的基石

資料庫事務 ACID 中的 Isolation (隔離性) 依賴鎖機制。無鎖並發會導致:

  • 更新丟失:事務 A 提交被事務 B 覆蓋。
  • 髒讀:讀到未提交數據。
  • 不可重複讀:同一查詢前後結果不一致 (更新/刪除)。
  • 幻讀:同一查詢前後行數變化 (插入)。

資料庫鎖分類

鎖粒度:行鎖、頁鎖、表鎖、庫鎖。InnoDB 默認行鎖 (索引生效時)。

Hibernate 中的鎖策略

1. 悲觀鎖

假設衝突高發,讀數據即加鎖。Hibernate 通過 LockMode 實現:


// 讀取時加排他鎖 (SELECT ... FOR UPDATE)
Account acc = session.get(Account.class, id, LockMode.PESSIMISTIC_WRITE);
// 或 LockMode.UPGRADE (數據庫相關,通常為 FOR UPDATE)
Account acc2 = session.get(Account.class, id, LockMode.UPGRADE);
  • 優點:數據庫層面強一致,簡單直接。
  • 缺點:並發性能差,長事務易死鎖,不適用高併發讀多寫少場景。

2. 樂觀鎖

假設衝突低發,提交時檢查版本。Hibernate 原生支持 @Version:


@Entity
@Table(name = "tb_account")
public class Account {
    @Id @Column(name = "id") private Long id;
    @Column(name = "balance") private Long balance;
    @Version @Column(name = "version") private Integer version; // 必須非空
    // getter/setter
}
  • 機制:更新時 SQL 帶 WHERE id=? AND version=?,version 自增。行數為 0 拋 StaleObjectStateException。
  • 優點:無鎖開銷,高併發讀優,適合長事務 (用戶編輯表單)。
  • 缺點:需業務處理衝突重試 (刷新對象再提交),ABA 問題 (極少見)。

3. 鎖模式對比實戰


// 樂觀鎖測試:兩線程同時讀取 version=1,餘額 100
// 線程1: balance=200, version=2 -> 更新成功
// 線程2: balance=50, version=2 -> 更新失敗,拋 StaleObjectStateException
// 業務捕獲異常 -> 重新加載對象 -> 重新應用業務邏輯 -> 再次提交

Hibernate 三大查詢方式對比

鎖類型別名用途兼容性
共享鎖S Lock / 讀鎖SELECT ... LOCK IN SHARE MODES-S 兼容,S-X 互斥
排他鎖X Lock / 寫鎖INSERT/UPDATE/DELETE / SELECT ... FOR UPDATEX 與任何鎖互斥
更新鎖U Lock預備更新,防止 S-S 死鎖,升級為 XU-S 兼容,U-X/U-U 互斥
維度HQL (Hibernate Query Language)QBC/QBE (Criteria/Example)Native SQL
語法風格面向對象,類 SQL程序化 API / 示例對象純數據庫 SQL
跨數據庫可移植性高 (Hibernate 翻譯)高低 (綁定方言)
動態查詢構建字符串拼接困難極佳 (CriteriaBuilder)字符串拼接
性能調優依賴 Hibernate 生成 SQL,難控制同 HQL完全可控,DBA 友好
類型安全運行時檢查編譯時類型安全 (JPA Criteria)無
適用場景靜態/半靜態查詢、報表複雜動態條件組合、通用查詢頁面複雜報表、存儲過程、特定方言優化、遺留系統

代碼示例

HQL


// 命名參數、分頁、投影
Query q = session.createQuery("FROM Account a WHERE a.balance > :min ORDER BY a.id DESC");
q.setParameter("min", 100L);
q.setFirstResult(0).setMaxResults(10);
List list = q.list();

QBC (Criteria API - JPA 2.0+ 標準)


CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Account.class);
Root root = cq.from(Account.class);
cq.where(cb.gt(root.get("balance"), 100L))
  .orderBy(cb.desc(root.get("id")));
List list = session.createQuery(cq)
    .setFirstResult(0).setMaxResults(10).list();

QBE (Query By Example)


Account example = new Account();
example.setBalance(100L); // 查詢餘額=100
Example ex = Example.create(example)
    .enableLike(MatchMode.ANYWHERE) // 字符串模糊匹配
    .excludeZeroes() // 排除基本類型零值
    .ignoreCase(); // 忽略大小寫
List list = session.createCriteria(Account.class).add(ex).list();

Native SQL


// 實體映射
List list = session.createSQLQuery(
    "SELECT * FROM tb_account WHERE balance > ? ORDER BY id DESC")
    .addEntity(Account.class)
    .setParameter(0, 100L)
    .setFirstResult(0).setMaxResults(10)
    .list();

// 純標量/DTO 映射
List rows = session.createSQLQuery(
    "SELECT id, balance FROM tb_account WHERE balance > ?")
    .addScalar("id", StandardBasicTypes.LONG)
    .addScalar("balance", StandardBasicTypes.LONG)
    .setParameter(0, 100L).list();

生產環境選型建議

  1. 優先 HQL/JPQL:業務邏輯查詢,可移植,配合 @NamedQuery 集中管理。
  2. 動態條件用 CriteriaBuilder:類型安全,避免字符串拼接注入風險。
  3. 複雜報表/大數據統計用 Native SQL:窗口函數、CTE、特定索引提示、存儲過程。
  4. 避免 QBE:功能受限,性能不可控,Hibernate 6 已棄用。
  5. 分頁必加 Order By:否則結果不穩定。
  6. N+1 問題:關聯查詢用 JOIN FETCH (HQL) 或 EntityGraph (JPA) 預加載。

總結

Hibernate 鎖機制定位於事務隔離層:悲觀鎖 (PESSIMISTIC_WRITE) 適用短事務強一致,樂觀鎖 (@Version) 適用長會話高併發。查詢方式上,HQL/JPQL 為主,CriteriaBuilder 處理動態條件,Native SQL 解決疑難性能。理解底層 SQL 生成與鎖行為,結合數據庫隔離級別 (RR/RC),方能構建高併發穩健的持久層。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved