目錄
寫在前面
上篇文章介紹了多對多關系的關聯查詢的sql,HQL,Criteria查詢的三種方式。本篇文章將介紹nhibernate中的延遲加載方式,延遲加載按個人理解也可以叫做按需要加載(Loading-on-demand)。
文檔與系列文章
[NHibernate]持久化類(Persistent Classes)
[NHibernate]集合類(Collections)映射
[NHibernate]緩存(NHibernate.Caches)
[NHibernate]NHibernate.Tool.hbm2net
[NHibernate]Nhibernate如何映射sqlserver中image字段
[NHibernate]條件查詢Criteria Query
延遲加載
延遲加載(Lazy Load)是(也成為懶加載)Hibernate3關聯關系對象默認的加載方式,延遲加載機制是為了避免一些無謂的性能開銷而提出來的,所謂延遲加載就是當在真正需要數據的時候,才真正執行數據加載操作。可以簡單理解為,只有在使用的時候,才會發出sql語句進行查詢。
延遲加載的有效期是在session打開的情況下,當session關閉后,會報異常。當調用load方法加載對象時,返回代理對象,等到真正用到對象的內容時才發出sql語句。
Hibernate2實現延遲加載有2種方式:1實體對象,2集合。
Hibernate3中又引入了一種新的加載方式:3屬性的延遲加載。
一般使用load的方法來實現延遲加載,在實現無限級聯動使用延遲加載效率比較好。
——百度百科(java,hibernate)
上面是java中對hibernate的延遲加載的描述,說的比我好多了。
記住三點:1,為了避免無謂的性能開銷。2,需要時才真正加載數據。3,使用了代理。
一個例子
一對多關系
默認延遲加載
采用懶加載的方式根據客戶id得到客戶信息
1 /// <summary> 2 /// 采用懶加載的方式根據客戶id得到客戶信息 3 /// </summary> 4 /// <param name="customerID"></param> 5 /// <returns></returns> 6 public Customer GetCustomerbyLazyLoad(Guid customerID) 7 { 8 //獲得ISession實例 9 ISession session = NHibernateHelper.GetSession(); 10 Customer customer = session.Get<Customer>(customerID); 11 return customer; 12 }
測試用代碼,如圖所示,此時數據已經加載出來了
使用SQL Profile監控生成的sql語句,截圖如下
你會發現,此時只生成了查詢customer的sql語句,nhibernate默認是使用延遲加載的,在前面的文章中,並沒在映射文件中設置節點的lazy屬性。
當展開customer的屬性Orders時或者調試向下移動的時候,會執行查詢,你會看到如下的sql語句
延遲加載並關閉Session
1 /// <summary> 2 /// 采用using釋放session,懶加載的方式根據客戶id得到客戶信息 3 /// </summary> 4 /// <param name="customerID"></param> 5 /// <returns></returns> 6 public Customer GetCustomerbyLazyLoadUsing(Guid customerID) 7 { 8 //獲得ISession實例 9 using(ISession session = NHibernateHelper.GetSession()) 10 { 11 Customer customer = session.Get<Customer>(customerID); 12 return customer; 13 } 14 }
測試,當視圖展開customer的orders屬性,或者往下執行獲得order集合時出錯。
延遲加載,需要的時候再去加載,因為此時session已經關閉,沒有去查詢的通道了,結果是“此路不通”的提示。
多對多關系
默認延遲加載
這里采用多對多關系那篇文章中舉的 Order和Product的例子。
1 /// <summary> 2 /// 多對多關系,延遲加載訂單產品 3 /// </summary> 4 /// <returns></returns> 5 public Order GetOrderByLazyLoad(Guid orderId) 6 { 7 try 8 { 9 var session = NHibernateHelper.GetSession(); 10 return session.Get<Wolfy.Shop.Domain.Entities.Order>(orderId); 11 } 12 catch (Exception) 13 { 14 throw; 15 } 16 }
進行測試,在查詢的時候只有Order表的數據(這個地方,有Order數據的sql,猜測在一對多查詢的時候,沒有出現查詢Customer的sql語句,很有可能是因為緩存的問題造成,因為經常使用Cusomer那條數據進行測試。)
展開Order的屬性
當展開Order的屬性,回去查詢order下的所有的Product,此時生成的sql如下:
延遲加載並關閉Session
1 /// <summary> 2 /// 多對多關系,延遲加載訂單產品 3 /// </summary> 4 /// <returns></returns> 5 public Order GetOrderByLazyLoadUsing(Guid orderId) 6 { 7 //session使用后即釋放 8 using (var session = NHibernateHelper.GetSession()) 9 { return session.Get<Wolfy.Shop.Domain.Entities.Order>(orderId); } 10 }
測試
此時生成的sql語句
如果此時展開Products,同樣會出現上面的異常
通過上面的比較一對多和多對多的默認延遲加載和關閉session后的情況類似。
N+1次select查詢問題
如果Order下有很多個Product,而我們就想需要的時候才去加載其中某些Product的信息,如果采用立即加載的方式,勢必產生多個sql語句,例如第一次查詢得到所有的Order對象,然后根據orderid去查詢得到所有的產品。
測試,多對多延遲加載訂單和訂單下單價大於6666的產品
1 protected void btnMany2Many_Click(object sender, EventArgs e) 2 { 3 Business.OrderBusiness orderBusiness = new Business.OrderBusiness(); 4 Order order = orderBusiness.GetOrderByLazyLoad(new Guid("78A53F67-A293-48A1-BBE2-86FED77342FA")); 5 decimal sum = 0; 6 foreach (var item in order.Products) 7 { 8 if (item.Price >= 6666) 9 { 10 sum += item.Price; 11 } 12 } 13 Response.Write(sum.ToString()); 14 }
生成的sql語句
延遲加載,可以解決select N+1的問題,比如你查詢一個訂單還有訂單下的Product,勢必會產生多條sql語句,先是查詢order的sql語句,然后是查詢product的sql語句,就會頻繁的查詢數據庫,造成性能方面的壓力,而采用延遲加載,通過上面生成的sql語句你會發現,使用left out join 將關聯的表拼接起來,只生成了一條sql。
總結
本篇文章介紹了Nhibernate在延遲加載方面的內容,Nhibernate在使用過程中延遲加載方式是默認的。對延遲加載的定義,需要再慢慢的體會。
延遲加載(Lazy Load)是(也成為懶加載)Hibernate3關聯關系對象默認的加載方式,延遲加載機制是為了避免一些無謂的性能開銷而提出來的,所謂延遲加載就是當在真正需要數據的時候,才真正執行數據加載操作。可以簡單理解為,只有在使用的時候,才會發出sql語句進行查詢。
延遲加載的有效期是在session打開的情況下,當session關閉后,會報異常。當調用load方法加載對象時,返回代理對象,等到真正用到對象的內容時才發出sql語句。
Hibernate2實現延遲加載有2種方式:1實體對象,2集合。
Hibernate3中又引入了一種新的加載方式:3屬性的延遲加載。
一般使用load的方法來實現延遲加載,在實現無限級聯動使用延遲加載效率比較好。
——百度百科(java,hibernate)