目錄
寫在前面
上篇文章介紹了nhibernate延遲加載的相關內容,簡單回顧一下延遲加載,就是需要的時候再去加載,需要的時候再向數據庫發出sql指令進行查詢。
本篇文章介紹的立即加載,則和延遲加載相對,舉個簡單的例子,就是查詢客戶信息,則會將該客戶相關聯的數據立即進行加載。實現立即加載的方式有三種:設置映射文件中節點的可選屬性lazy,Nhibernate提供的實用類,HQL抓取策略。
文檔與系列文章
[NHibernate]持久化類(Persistent Classes)
[NHibernate]集合類(Collections)映射
[NHibernate]緩存(NHibernate.Caches)
[NHibernate]NHibernate.Tool.hbm2net
[NHibernate]Nhibernate如何映射sqlserver中image字段
[NHibernate]條件查詢Criteria Query
立即加載
一個例子
1、一對多關系
通過立即加載的方式,加載客戶信息及客戶的訂單信息。
1 /// <summary> 2 /// 采用立即加載的方式加載客戶信息 3 /// </summary> 4 /// <param name="customerID"></param> 5 /// <returns></returns> 6 public Customer GetCustomerByImmediatelyLoad(Guid customerID) 7 { 8 //獲得ISession實例 9 //通過using的方式將session釋放,為了保證是立即加載的數據,而不是延遲加載的 10 //還記得上篇文章中提到的在延遲加載中,在關閉session情況下,再讀取數據的時候就會有一個異常信息 11 //忘記的可以再回到上一篇文章進行查看 12 using (ISession session = NHibernateHelper.GetSession()) 13 { 14 return session.Get<Customer>(customerID); 15 } 16 }
1.1、修改Customer.hbm.xml文件
因為nhibernate默認是使用Lazy的方式加載數據的,也就是默認Lazy=“true”的,需要顯示的指定Lazy=“false”。
1 <!--一對多關系:一個客戶可以有一個或者多個訂單--> 2 <!--子實體負責維護關聯關系--> 3 <set name="Orders" table="TB_Order" generic="true" inverse="true" cascade="all" lazy="false"> 4 <key column="CustomerID" foreign-key="FK_TB_Order_TB_Customer"></key> 5 <one-to-many class="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain"/> 6 </set>
編寫一個測試驗證,調用數據訪問層中的使用using強制資源清理Session加載Customer對象的方法加載一個Customer對象,NHibernate這時立即加載Customer相關聯的Order對象。
利用NHibernate提供實用類(NHibernateUtil)測試被關聯的Customer對象集合是否已初始化(也就是已加載)。
上篇文章中也說明了,如果在關閉session的情況下,如果你展開customer的orders屬性,就會出現異常
這也說明了Orders是跟customer一樣立即加載的。
查看生成的sql語句
1 exec sp_executesql N'SELECT customer0_.CustomerID as CustomerID0_0_, customer0_.Version as Version0_0_, customer0_.CustomerName as Customer3_0_0_, customer0_.CustomerAddress as Customer4_0_0_ FROM TB_Customer customer0_ WHERE customer0_.CustomerID=@p0',N'@p0 uniqueidentifier',@p0='B0720295-9541-40B3-9994-610066224DB8' 2 3 exec sp_executesql N'SELECT orders0_.CustomerID as CustomerID1_, orders0_.OrderID as OrderID1_, orders0_.OrderID as OrderID1_0_, orders0_.OrderDate as OrderDate1_0_, orders0_.CustomerID as CustomerID1_0_ FROM TB_Order orders0_ WHERE orders0_.CustomerID=@p0',N'@p0 uniqueidentifier',@p0='B0720295-9541-40B3-9994-610066224DB8'
第一條sql為查詢Customer的,第二條sql是查詢與該customer相關聯的order的。
1.2、使用NHibernateUtil實用類
NHibernate提供實用類(NHibernateUtil)可以用來檢測被關聯的對象集合是否已初始化,還可以強制初始化未初始化的相關聯的對象。有了這個功能,我們就可以修改數據訪問層中的方法,把上面使用Using強制清理關閉Session的方法中加上NHibernateUtil類提供Initialize方法來初始化Customer相關聯的Order對象集合。
修改Customer.hbm.xml文件,將set節點的lazy屬性設置為默認值true,或者刪除該屬性。
1 /// <summary> 2 /// NHibernateUtil方式,立即加載客戶信息及關聯的數據 3 /// </summary> 4 /// <param name="customerID"></param> 5 /// <returns></returns> 6 public Customer GetCustomerByImmediatelyLoadNHibernateUtil(Guid customerID) 7 { 8 //獲得ISession實例 9 //通過using的方式將session釋放,為了保證是立即加載的數據,而不是延遲加載的 10 //還記得上篇文章中提到的在延遲加載中,在關閉session情況下,再讀取數據的時候就會有一個異常信息 11 //忘記的可以再回到上一篇文章進行查看 12 using (ISession session = NHibernateHelper.GetSession()) 13 { 14 Customer customer = session.Get<Customer>(customerID); 15 // 16 // 摘要: 17 // Force initialization of a proxy or persistent collection. 18 // 19 NHibernate.NHibernateUtil.Initialize(customer.Orders); 20 return customer; 21 } 22 }
通過該種方式,跟上面的測試結果相同,就不再貼圖了。
2、多對多關系
2.1、使用Lazy=“false”屬性
例子同上面的類似,就不再贅述。
2.2、使用NHibernateUtil實用類
這里采用Order和Product多對多實例進行分析。
如果想在加載Order的情況下,加載該Order下的所有Product,可以使用NHibernateUtil類初始化關聯對象(把他們從數據庫取出來)。
1 /// <summary> 2 /// NHibernateUtil方式,立即加載訂單與商品 3 /// </summary> 4 /// <param name="customerID"></param> 5 /// <returns></returns> 6 public Order GetOrderProductByImmediatelyLoadNHibernateUtil(Guid orderId) 7 { 8 using (var session = NHibernateHelper.GetSession()) 9 { 10 var order = session.Get<Wolfy.Shop.Domain.Entities.Order>(orderId); 11 //強制初始化customer 12 NHibernate.NHibernateUtil.Initialize(order.Customer); 13 //強制初始化Product 14 NHibernate.NHibernateUtil.Initialize(order.Products); 15 return order; 16 } 17 }
測試
查看生成的sql語句
1 exec sp_executesql N'SELECT order0_.OrderID as OrderID1_0_, order0_.OrderDate as OrderDate1_0_, order0_.CustomerID as CustomerID1_0_ FROM TB_Order order0_ WHERE order0_.OrderID=@p0',N'@p0 uniqueidentifier',@p0='78A53F67-A293-48A1-BBE2-86FED77342FA' 2 3 exec sp_executesql N'SELECT customer0_.CustomerID as CustomerID0_0_, customer0_.Version as Version0_0_, customer0_.CustomerName as Customer3_0_0_, customer0_.CustomerAddress as Customer4_0_0_ FROM TB_Customer customer0_ WHERE customer0_.CustomerID=@p0',N'@p0 uniqueidentifier',@p0='B0720295-9541-40B3-9994-610066224DB8' 4 5 exec sp_executesql N'SELECT products0_.OrderID as OrderID1_, products0_.ProductID as ProductID1_, product1_.ProductID as ProductID3_0_, product1_.Name as Name3_0_, product1_.Price as Price3_0_ FROM TB_OrderProduct products0_ left outer join TB_Product product1_ on products0_.ProductID=product1_.ProductID WHERE products0_.OrderID=@p0',N'@p0 uniqueidentifier',@p0='78A53F67-A293-48A1-BBE2-86FED77342FA'
這里生成了三條sql語句,查詢和Order關聯的customer和Product。
2.3使用HQL抓取策略
HQL語句支持的連接類型為:inner join(內連接)、left outer join(左外連接)、right outer join(右外連接)、full join(全連接,不常用)。
“抓取fetch”連接允許僅僅使用一個選擇語句就將相關聯的對象隨着他們的父對象的初始化而被初始化,可以有效的代替了映射文件中的外聯接與延遲屬性聲明。
幾點注意:
- fetch不與setMaxResults() 或setFirstResult()共用,因為這些操作是基於結果集的,而在預先抓取集合時可能包含重復的數據,也就是說無法預先知道精確的行數。
- fetch還不能與獨立的with條件一起使用。通過在一次查詢中fetch多個集合,可以制造出笛卡爾積,因此請多加注意。對多對多映射來說,同時join fetch多個集合角色可能在某些情況下給出並非預期的結果,也請小心。
- 使用full join fetch 與 right join fetch是沒有意義的。 如果你使用屬性級別的延遲獲取,在第一個查詢中可以使用 fetch all properties 來強制NHibernate立即取得那些原本需要延遲加載的屬性。
參考:http://www.cnblogs.com/lyj/archive/2008/10/29/1322373.html
測試代碼
1 /// <summary> 2 /// HQL方式,立即加載訂單與商品 3 /// </summary> 4 /// <param name="customerID"></param> 5 /// <returns></returns> 6 public Order GetOrderProductByImmediatelyLoadHQL(Guid orderId) 7 { 8 using (var session = NHibernateHelper.GetSession()) 9 { 10 return session.CreateQuery("from Order o" + 11 " left outer join fetch o.Products" + 12 " inner join fetch o.Customer where o.OrderID=:orderId") 13 .SetGuid("orderId", orderId) 14 .UniqueResult<Order>(); 15 } 16 }
生成的sql語句
總結
這篇文章介紹了nhibernate立即加載的方式,什么時候使用立即加載,什么時候使用延遲加載,視項目中的情況而定。
參考文章:http://www.cnblogs.com/lyj/archive/2008/10/29/1322373.html