上一篇介紹了 NHibernate 默認的延遲加載機制(基於 Proxy 模式),本文將系統分析立即加載的三種實現方式:lazy="false" 屬性、NHibernateUtil 工具類、HQL 抓取策略。
立即加載即在載入主對象時同步載入關聯集合,與延遲加載相反。通過一對多、多對多兩類關係實例深度解析。
修改 Customer.hbm.xml 中 <set> 元素:
<set name="Orders" ... lazy="false">
測試代碼:
[Test]
public void EagerLoadUsingLazyFalseTest()
{
Customer customer = _relation.LazyLoadUsingSession(1);
Assert.IsTrue(NHibernateUtil.IsInitialized(customer.Orders));
}
結果:生成兩條 SQL,分別查詢 Customer 與關聯 Orders。集合類型為 Iesi.Collections.Generic.HashedSet,但若在 using 區塊外訪問會拋出異常,驗證了立即加載在 Session 關閉前完成數據獲取。
修改 DAL 方法,在 Session 關閉前調用初始化:
public Customer EagerLoadUsingSessionAndNHibernateUtil(int customerId)
{
using (ISession _session = new SessionManager().GetSession())
{
Customer customer = _session.Get<Customer>(customerId);
NHibernateUtil.Initialize(customer.Orders);
return customer;
}
}
測試結果與方法一致,適用於映射文件不可修改或需動態控制的場景。
同理在多對多映射的 <bag> 設定 lazy="false"。注意:關閉延遲加載後,持久化類屬性可不再強制 virtual,但建議保持默認延遲加載以獲取性能優勢,僅在特殊場景啟用立即加載。
DAL 方法同時初始化 Customer(多對一)與 Products(多對多):
public Order EagerLoadOrderAggregateSessionAndNHibernateUtil(int orderId)
{
using (ISession _session = new SessionManager().GetSession())
{
Order order = _session.Get<Order>(orderId);
NHibernateUtil.Initialize(order.Customer);
NHibernateUtil.Initialize(order.Products);
return order;
}
}
SQL 分析:一次立即加載生成四條 SQL,分別查詢 Order、Customer、OrderProduct 關聯表與 Product。雖獲取完整對象圖,但可能載入未使用數據,需權衡性能。
使用 fetch 關鍵字在單次查詢中完成多表連接:
public Order EagerLoadOrderAggregateWithHQL(int orderId)
{
using (ISession _session = new SessionManager().GetSession())
{
return _session.CreateQuery("from Order o " +
"left outer join fetch o.Products " +
"inner join fetch o.Customer where o.OrderId=:orderId")
.SetInt32("orderId", orderId)
.UniqueResult<Order>();
}
}
setMaxResults() 或 setFirstResult() 共用(結果集可能含重複行)with 條件共用fetch 可能產生笛卡爾積join fetch 多角色可能結果非預期full join fetch / right join fetch 無實際意義fetch all properties 強制立即獲取單條 SQL 完成 Order、Customer、Products 三表連接查詢,極大減少資料庫往返:
SELECT ... FROM [Order] order0_
LEFT OUTER JOIN OrderProduct products1_ ON ...
LEFT OUTER JOIN Product product2_ ON ...
INNER JOIN Customer customer3_ ON ...
WHERE order0_.OrderId = @p0
三種立即加載方案對比:
| 方案 | 適用場景 | SQL 效率 | 靈活性 |
|---|---|---|---|
| lazy="false" | 全局固定策略 | 多條 SQL | 低(靜態配置) |
| NHibernateUtil | 動態控制、映射不可改 | 多條 SQL | 中(代碼控制) |
| HQL Fetch | 複雜關聯、追求極致性能 | 單條 SQL(連接查詢) | 高(查詢級控制) |
建議:默認使用延遲加載,按需採用 HQL Fetch 解決 N+1 問題,特殊場景輔以 NHibernateUtil。