首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

NHibernate實戰(12):延遲加載機制深度探秘

     編輯:關於.NET

前言

基於經典 Customer-Order-Product 數據模型,本文從一對多、多對多兩個維度深度剖析 NHibernate 默認的延遲加載機制,揭示 Proxy 代理模式實現原理與常見陷阱。

數據模型回顧

  • Customer 1∶N Order(一對多)
  • Order N∶M Product(多對多)

延遲加載核心原理

NHibernate 1.2+ 默認啟用延遲加載,本質採用 GoF 23 種設計模式中的代理模式。當訪問關聯屬性時,動態生成代理對象,僅在真正需要時發送 SQL 查詢。

一對多場景:Customer → Orders

DAL 方法定義

// 正常 Session 生命週期
public Customer LazyLoad(int customerId) 
    => _session.Get<Customer>(customerId);

// 強制關閉 Session 模擬 Web 請求結束
public Customer LazyLoadUsingSession(int customerId)
{
    using (ISession session = new SessionManager().GetSession())
        return session.Get<Customer>(customerId);
}

場景一:默認延遲加載(Session 開啟)

[Test]
public void LazyLoadTest()
{
    Customer customer = _relation.LazyLoad(1);
    Assert.IsFalse(NHibernateUtil.IsInitialized(customer.Orders));
}
  • SQL:僅查詢 Customer 表單條語句
  • 集合類型:Iesi.Collections.Generic.HashedSet<Order>
  • 調試可見:集合內容已加載(NHibernate 內部優化預載)

場景二:Session 關閉後訪問(LazyInitializationException)

[Test]
public void LazyLoadUsingSessionTest()
{
    Customer customer = _relation.LazyLoadUsingSession(1);
    Assert.IsFalse(NHibernateUtil.IsInitialized(customer.Orders));
}
  • SQL:同場景一
  • 集合類型:NHibernate.Collection.Generic.PersistentGenericSet<Order>
  • 關鍵差異:嘗試訪問集合項拋出 HibernateException: failed to lazily initialize a collection, no session or session was closed

多對多場景:Order ↔ Products & Customer

DAL 方法

public Order LazyLoadOrderAggregate(int orderId)
    => _session.Get<Order>(orderId);

public Order LazyLoadOrderAggregateUsingSession(int orderId)
{
    using (ISession session = new SessionManager().GetSession())
        return session.Get<Order>(orderId);
}

場景一:Session 開啟時

  • SQL:單條查詢 Order 表
  • Customer 類型:代理對象 CustomerProxy...(多對一延遲)
  • Products 類型:Iesi.Collections.Generic.HashedSet<Product>
  • 驗證:NHibernateUtil.IsInitialized 均返回 false

場景二:Session 關閉後

  • Customer 訪問異常:LazyInitializationException: Could not initialize proxy - no Session
  • Products 訪問異常:HibernateException: failed to lazily initialize a collection
  • 集合類型:PersistentGenericBag<Product>

場景三:N+1 查詢問題演示

[Test]
public void LazyLoadOrderAggregateSelectBehaviorTest()
{
    Order order = _relation.LazyLoadOrderAggregate(2);
    float sum = 0.0F;
    foreach (var item in order.Products)
        sum += item.Cost;
    Assert.AreEqual(21.0F, sum);
}

SQL 結果:NHibernate 智能優化,單條左連接查詢加載所有 Product,避免 N+1。但大集合場景下仍存在效能隱患。

核心異常總結

場景關聯類型集合/代理類型Session 關閉後訪問
一對多SetPersistentGenericSetHibernateException
多對一ProxyCustomerProxy...LazyInitializationException
多對多BagPersistentGenericBagHibernateException

結語

延遲加載雖節省初始查詢資源,但帶來 Session 管理複雜度與 N+1 風險。下篇將介紹立即加載機制,提供解決方案。關鍵實踐建議:

  1. Web 應用採用 Open Session in View 模式延長 Session 生命週期
  2. DTO 投影查詢避免實體關聯加載
  3. 批量處理場景显式使用立即加載策略
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved