Hibernate 作為 JDO 實現,將對象模型映射到關係模型。其核心優勢在於:開發者操作的是領域對象圖,而非 JDBC 的 ResultSet。多表關聯查詢是這一映射威力最大的體現——一條 HQL 替代多條 SQL + 手動裝配。
// 產生笛卡爾積,數據量大時極其危險
from Student, Book
from Student as stu, Book as boo
from Student stu, Book boo
注意:實體類名區分大小寫,別名遵循駝峰命名 (首字母小寫)。
// 內連接:僅返回有匹配關聯的記錄
from Student s inner join s.books b
// 左外連接:返回所有學生,無書籍則 book 為 null
from Student s left join fetch s.books b
// 帶條件查詢
from Student s join s.books b where b.publishDate > :date
圖書管理系統:tBookInfo (圖書基本信息) 與 BookSelection (選書記錄) 為一對多關聯。
String hql = "from tBookInfo book, BookSelection sel where book.id = sel.bookId";
Session session = HibernateUtil.currentSession();
Transaction tx = null;
List
List,而非實體列表fetch 可預加載關聯集合,避免 N+1 問題// 定義 DTO
public class BookSelectionDTO {
private String title;
private String selectionId;
public BookSelectionDTO(String title, String selectionId) {
this.title = title;
this.selectionId = selectionId;
}
// getters...
}
// HQL 直接實例化 DTO
String hql = "select new com.dto.BookSelectionDTO(book.title, sel.id) " +
"from tBookInfo book join book.selections sel";
List dtos = query.list(); // 類型安全,無需強轉
// tBookInfo.hbm.xml
// BookSelection.hbm.xml
// 業務代碼:純對象導航,無需寫 HQL
tBookInfo book = session.get(tBookInfo.class, bookId);
for (BookSelection sel : book.getSelections()) { // 惰性加載觸發 SQL
System.out.println(sel.getId());
}
| 問題 | 原因 | 解決方案 |
|---|---|---|
| N+1 查詢 | 惰性加載關聯集合時逐條發 SQL | HQL 用 join fetch / 設置 lazy="false" / 批量抓取 batch-size |
| 笛卡爾積爆炸 | 多包關聯同時 fetch 產生行數乘積 | 分批 fetch / 使用子查詢 / 二級緩存 |
| 重複數據傳輸 | 一對多連接導致主表列重複 | DTO 投影只選所需列 / 使用 DistinctRootEntityResultTransformer |
| 異常事務管理 | 查詢異常未回滾事務 | 統一模板:try { tx.begin(); ... tx.commit(); } catch { tx.rollback(); throw; } finally { close(); } |
Criteria criteria = session.createCriteria(tBookInfo.class, "book");
criteria.createAlias("book.selections", "sel", Criteria.LEFT_JOIN);
criteria.add(Restrictions.eq("book.id", "sel.bookId")); // 實際上映射已定義關聯
// 投影到 DTO
criteria.setProjection(Projections.projectionList()
.add(Projections.property("book.title"), "title")
.add(Projections.property("sel.id"), "selectionId")
);
criteria.setResultTransformer(Transformers.aliasToBean(BookSelectionDTO.class));
List list = criteria.list();
Hibernate 多表查詢的精髓在於:用對象關聯思維替代 SQL 連接思維。掌握 join fetch、DTO 投影、實體導航三種模式,配合二級緩存與批量抓取,即可優雅解決 95% 的關聯查詢場景,徹底告別手動 ResultSet 裝配的痛苦。