基於 Customer-Order 父子關係,本文系統講解 NHibernate 三大查詢方式(原生 SQL、HQL、Criteria API)在關聯查詢中的具體應用,並深入探討 Criteria API 的結果轉換與投影技巧。
查詢擁有指定日期後訂單的客戶列表,Customer 1∶N Order。
public IList<Customer> UseSQL_GetCustomersWithOrders(DateTime orderDate)
{
return _session.CreateSQLQuery(
"select distinct {customer.*} from Customer {customer}" +
" inner join [Order] o on o.Customer={customer}.CustomerId" +
" where o.OrderDate > :orderDate")
.AddEntity("customer", typeof(Customer))
.SetDateTime("orderDate", orderDate)
.List<Customer>();
}
關鍵語法:{customer.*} 代表 Customer 所有屬性,AddEntity 映射結果集到實體。
public IList<Customer> UseHQL_GetCustomersWithOrders(DateTime orderDate)
{
return _session.CreateQuery(
"select distinct c from Customer c, " +
"c.Orders.elements o where o.OrderDate > :orderDate")
.SetDateTime("orderDate", orderDate)
.List<Customer>();
}
測試驗證:斷言查詢結果客戶數為 1,且其訂單日期滿足條件。
public IList<Customer> UseCriteriaAPI_GetCustomersWithOrders(DateTime orderDate)
{
return _session.CreateCriteria(typeof(Customer))
.CreateCriteria("Orders")
.Add(Restrictions.Gt("OrderDate", orderDate))
.List<Customer>();
}
問題:一對多關聯導致父實體重複,生成 SQL 無 DISTINCT。
public IList<Customer> UseCriteriaAPI_GetCustomersWithOrders(DateTime orderDate)
{
return _session.CreateCriteria(typeof(Customer))
.CreateCriteria("Orders")
.Add(Restrictions.Gt("OrderDate", orderDate))
.SetResultTransformer(new NHibernate.Transform.DistinctRootEntityResultTransformer())
// 或 .SetResultTransformer(NHibernate.CriteriaUtil.DistinctRootEntity)
.List<Customer>();
}
原理:記憶體層面去重,不修改 SQL,適用於分頁查詢前預處理。
public IList<Customer> UseCriteriaAPI_GetDistinctCustomers(DateTime orderDate)
{
// 步驟1:查詢去重後的 CustomerId 集合
IList<int> ids = _session.CreateCriteria(typeof(Customer))
.SetProjection(Projections.Distinct(Projections.ProjectionList()
.Add(Projections.Property("CustomerId"))))
.CreateCriteria("Orders")
.Add(Restrictions.Gt("OrderDate", orderDate))
.List<int>();
// 步驟2:根據 ID 列表查詢完整實體
return _session.CreateCriteria(typeof(Customer))
.Add(Restrictions.In("CustomerId", ids.ToArray<int>()))
.List<Customer>();
}
優勢:SQL 層面 DISTINCT,減少傳輸數據量,適合大數據集場景。Projections 工廠提供豐富聚合函數。
| 技術 | 適用場景 | SQL 影響 | 性能特點 |
|---|---|---|---|
| CreateCriteria 導航 | 動態多層關聯約束 | 生成 JOIN | 直觀易維護 |
| DistinctRootEntityResultTransformer | 記憶體去重、分頁前處理 | 無 DISTINCT | 需載入重複行 |
| Projection + Distinct + In 查詢 | 大數據集、需 SQL 層面去重 | 含 DISTINCT | 兩次查詢、傳輸少 |
一對多關聯查詢中,HQL 最貼近對象思維,Criteria API 最靈活強大。掌握結果轉換器與投影組合,能優雅解決重複結果難題。下篇將建立一對多映射基礎配置。