Hibernate 提供多層次查詢機制,滿足從簡單主鍵查詢到複雜報表分析的各類需求。本文系統整理 7 種查詢方式,附帶代碼範例、適用場景與效能建議。
// 立即查詢,無資料返回 null
User user = session.get(User.class, 1L);
// 延遲載入 (Proxy),無資料拋出 ObjectNotFoundException
User user = session.load(User.class, 1L);
| 特性 | get() | load() |
|---|---|---|
| 資料庫存取時機 | 立即 | 首次存取屬性時 (延遲) |
| 無資料行為 | 返回 null | 拋出異常 |
| 返回型別 | 實體物件 | Proxy 代理物件 |
| 批次載入支援 | 不支援 | 支援 (配合 batch-size) |
| 適用場景 | 不確定資料是否存在 | 確定存在、建立關聯不需即時查詢 |
面向物件的查詢語言,類 SQL 但操作實體與屬性。
// 位置參數
String hql = "FROM Order o WHERE o.id = ?";
Order order = (Order) session.createQuery(hql)
.setParameter(0, orderId)
.uniqueResult();
// 命名參數 (推薦:可讀性高、防注入)
Query query = session.createQuery("FROM User u WHERE u.name = :name");
query.setString("name", "John");
// IN 子句參數列表
Query query = session.createQuery("FROM Product p WHERE p.category IN (:cats)");
query.setParameterList("cats", categoryList);
// 分頁查詢
Query query = session.createQuery("FROM Article a ORDER BY a.createTime DESC");
query.setFirstResult(20); // 跳過前 20 筆 (第 3 頁,每頁 10 筆)
query.setMaxResults(10);
List<Article> list = query.list();
適合動態查詢條件 (如多條件搜尋表單)。
Criteria criteria = session.createCriteria(Cat.class);
// 條件組合
criteria.add(Restrictions.like("name", "Fritz%"));
criteria.add(Restrictions.or(
Restrictions.eq("age", 0),
Restrictions.isNull("age")
));
// 排序
criteria.addOrder(Order.asc("name"));
criteria.addOrder(Order.desc("age"));
// 分頁
criteria.setFirstResult(0);
criteria.setMaxResults(20);
List<Cat> cats = criteria.list();
// 投影查詢 (僅選取特定欄位)
criteria.setProjection(Projections.projectionList()
.add(Projections.property("id"), "id")
.add(Projections.property("name"), "name")
);
List<Object[]> rows = criteria.list();
處理資料庫特有語法、複雜報表、存儲過程、HQL 無法表達的查詢。
// 遞迴查詢 (Oracle CONNECT BY)
String sql = "SELECT t.*, LEVEL FROM tree t " +
"START WITH t.parent_id = 0 " +
"CONNECT BY PRIOR t.id = t.parent_id";
List<Object[]> result = session.createSQLQuery(sql)
.addEntity("t", Tree.class) // 映射實體
.addScalar("level", Hibernate.INTEGER) // 映射額外欄位
.list();
// 純標量查詢 (非實體)
List<Object[]> summary = session.createSQLQuery(
"SELECT COUNT(*), SUM(amount) FROM orders WHERE created > ?"
).addScalar("cnt", Hibernate.LONG)
.addScalar("sum", Hibernate.BIG_DECIMAL)
.setParameter(0, startDate)
.list();
在映射檔或註解中預定義,編譯期驗證、集中管理、效能優化 (預編譯)。
// 映射檔定義
<query name="User.findByEmail">
<![CDATA[ FROM User u WHERE u.email = :email ]]>
</query>
// 程式碼呼叫
Query query = session.getNamedQuery("User.findByEmail");
query.setString("email", "test@example.com");
User user = (User) query.uniqueResult();
全域、可選啟用的條件片段,常用於軟刪除、多租戶、資料權限。
// 映射檔定義
<filter name="activeFilter" condition=":isActive = 1"/>
<filter-def name="activeFilter">
<filter-param name="isActive" type="boolean"/>
</filter-def>
// 實體類別啟用
@Filter(name = "activeFilter", condition = "is_active = 1")
@Entity
public class Article { ... }
// 啟用/停用
session.enableFilter("activeFilter").setParameter("isActive", true);
List<Article> list = session.createQuery("FROM Article").list(); // 自動加 WHERE is_active=1
session.disableFilter("activeFilter");
無 Session 環境下構建查詢,稍後在任意 Session 執行。適合 Web 層構建查詢、Service 層執行。
// Web 層構建 (無 Session)
DetachedCriteria criteria = DetachedCriteria.forClass(Order.class);
criteria.add(Restrictions.ge("amount", 1000));
criteria.addOrder(Order.desc("createTime"));
// Service 層執行 (有 Session)
List<Order> orders = criteria.getExecutableCriteria(session).list();
| 場景 | 推薦方式 | 理由 |
|---|---|---|
| 主鍵查詢單一實體 | get() / load() | 最簡潔、一級快取支援 |
| 固定條件業務查詢 | Named Query (HQL) | 預編譯、集中維護、可讀性好 |
| 動態多條件搜尋 | Criteria / JPA CriteriaBuilder | 類型安全、程式化組合條件 |
| 複雜報表/資料庫特有語法 | Native SQL | 發揮資料庫威力、避免 HQL 限制 |
| 跨層傳遞查詢條件 | DetachedCriteria | 分離構建與執行 |
| 全域橫切關注點 (軟刪除/租戶) | Filter | 透明攔截、零侵入業務代碼 |
Hibernate 5.2+ / JPA 2.1 推薦使用標準 CriteriaBuilder API** (型別安全、功能對等),原生 Criteria 進入維護模式。新專案請優先採用 JPA 標準介面。