Hibernate 作為主流 ORM 框架,其多表查詢與抓取策略直接決定應用效能。本文系統整理 SQL 連接查詢對應的 HQL/JPQL 寫法,並深度解析延遲載入與抓取策略的配置細節,助您避開 N+1 查詢陷阱。
| 連接類型 | SQL 語法 | 說明 |
|---|---|---|
| 交叉連接 | SELECT * FROM A, B | 笛卡兒積,無條件 |
| 內連接 | SELECT * FROM A INNER JOIN B ON 條件 | 僅返回匹配行 |
| 隱式內連接 | SELECT * FROM A, B WHERE 條件 | 舊語法,不推薦 |
| 左外連接 | SELECT * FROM A LEFT OUTER JOIN B ON 條件 | 保留左表所有行 |
| 右外連接 | SELECT * FROM A RIGHT OUTER JOIN B ON 條件 | 保留右表所有行 |
// 顯式內連接
FROM Customer c INNER JOIN c.linkmans
// 隱式內連接(Theta-style)
FROM Customer c, LinkMan l WHERE c.id = l.customerId
// 迫切內連接(FETCH JOIN,一次查詢帶出關聯)
FROM Customer c INNER JOIN FETCH c.linkmans
// 左外連接
FROM Customer c LEFT OUTER JOIN c.linkmans
// 迫切左外連接
FROM Customer c LEFT JOIN FETCH c.linkmans
關鍵差異:FETCH 關鍵字會將關聯資料在同一 SQL 中查回,避免後續存取觸發額外查詢。
預設開啟,分兩個層級:
session.load() 回傳 Proxy,首次存取非 ID 屬性時才查詢。final 類或映射檔 lazy="false" 會失效。<set>、<many-to-one> 上的 lazy 屬性控制。預設 true(Proxy),可設 false(立即載入)或 extra(極度懶惰,size() 不載入集合)。在關聯映射標籤上配置 fetch 屬性,控制 SQL 產生方式:
| fetch 值 | 行為 | 適用場景 |
|---|---|---|
| select(預設) | 獨立 SELECT 查詢關聯 | 一般情況,配合 lazy=true |
| join | 迫切左外連接 | 確定需要關聯資料,避免 N+1 |
| subselect | 子查詢一次帶出所有關聯 | 批次載入多筆主物件的關聯 |
<set name="linkmans" batch-size="10">
查詢 10 筆 Customer 時,以 IN 子句一次查出所有關聯 LinkMan,大幅減少 SQL 次數。
| 屬性 | 選項 | 說明 |
|---|---|---|
| fetch | select / join | 同上 |
| lazy | proxy(預設)/ false / no-proxy | proxy 由對方類級別 lazy 決定;no-proxy 需建構期增強 |
lazy="true",按需在 HQL 中用 FETCH JOIN 或 fetch="join" 優化batch-size 而非 fetch="join"(避免笛卡兒積爆炸)Hibernate.initialize() 強制載入靈活運用抓取策略,能在開發效率與執行效能間找到最佳平衡點。