
- NHibernate 教程
- NHibernate - 首頁
- NHibernate - 概述
- NHibernate - 架構
- NHibernate - ORM
- NHibernate - 環境設定
- NHibernate - 快速入門
- NHibernate - 基本ORM
- NHibernate - 基本CRUD操作
- NHibernate - 分析器
- 為對映檔案新增IntelliSense
- NHibernate - 資料型別對映
- NHibernate - 配置
- NHibernate - 覆蓋配置
- NHibernate - 批處理大小
- NHibernate - 快取
- NHibernate - 元件對映
- NHibernate - 關係
- NHibernate - 集合對映
- NHibernate - 級聯操作
- NHibernate - 延遲載入
- NHibernate - 反向關係
- NHibernate - Load/Get
- NHibernate - LINQ
- NHibernate - 查詢語言
- NHibernate - Criteria 查詢
- NHibernate - QueryOver 查詢
- NHibernate - 原生SQL
- NHibernate - Fluent NHibernate
- NHibernate 有用資源
- NHibernate - 快速指南
- NHibernate - 有用資源
- NHibernate - 討論
NHibernate - 延遲載入
本章將介紹延遲載入功能。它是一個完全不同的概念,預設情況下NHibernate不啟用延遲載入,例如,如果您載入一個客戶,它不會載入所有訂單。
訂單集合將在需要時載入。
任何關聯,無論是多對一還是集合,預設情況下都是延遲載入的,它需要一個**開啟的ISession**。
如果您已關閉會話,或者已提交事務,您可能會收到一個延遲載入異常,因為它無法載入這些附加物件。
您必須注意延遲載入以及您實際需要多少資料。
您可以為整個關聯關閉延遲載入,或者您可以將lazy設定為false,或者您可以指定一個獲取策略。
這是**Program.cs**檔案的實現。
using System; using System.Data; using System.Linq; using System.Reflection; using HibernatingRhinos.Profiler.Appender.NHibernate; using NHibernate.Cfg; using NHibernate.Dialect; using NHibernate.Driver; using NHibernate.Linq; namespace NHibernateDemo { internal class Program { private static void Main() { var cfg = ConfigureNHibernate(); var sessionFactory = cfg.BuildSessionFactory(); Guid id; using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var newCustomer = CreateCustomer(); Console.WriteLine("New Customer:"); Console.WriteLine(newCustomer); session.Save(newCustomer); id = newCustomer.Id; tx.Commit(); } using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var reloaded = session.Load<Customer>(id); Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded); Console.WriteLine("The orders were ordered by: "); foreach (var order in reloaded.Orders) { Console.WriteLine(order.Customer); } tx.Commit(); } Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine(); } private static Customer CreateCustomer() { var customer = new Customer { FirstName = "John", LastName = "Doe", Points =100, HasGoldStatus = true, MemberSince = new DateTime(2012, 1, 1), CreditRating = CustomerCreditRating.Good, AverageRating = 42.42424242, Address = CreateLocation() }; var order1 = new Order { Ordered = DateTime.Now }; customer.AddOrder(order1); var order2 = new Order { Ordered = DateTime.Now.AddDays(-1), Shipped = DateTime.Now, ShipTo = CreateLocation() }; customer.AddOrder(order2); return customer; } private static Location CreateLocation() { return new Location { Street = "123 Somewhere Avenue", City = "Nowhere", Province = "Alberta", Country = "Canada" }; } private static Configuration ConfigureNHibernate() { NHibernateProfiler.Initialize(); var cfg = new Configuration(); cfg.DataBaseIntegration(x => { x.ConnectionStringName = "default"; x.Driver<SqlClientDriver>(); x.Dialect<MsSql2008Dialect<(); x.IsolationLevel = IsolationLevel.RepeatableRead; x.Timeout = 10; x.BatchSize = 10; }); cfg.SessionFactory().GenerateStatistics(); cfg.AddAssembly(Assembly.GetExecutingAssembly()); return cfg; } } }
為了理解這一點,讓我們執行應用程式並檢視NHibernate Profiler。

您可以看到,我們有Select From Customer,給定特定的客戶ID,然後我們在實際訪問該客戶的集合時還有另一個Select From Orders表。
所以我們對資料庫進行了兩次往返。現在,有時我們想最佳化它。為此,讓我們轉到**customer.hbm.xml**檔案並新增一個獲取策略,並要求它執行join fetch。
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" namespace = "NHibernateDemo"> <class name = "Customer"> <id name = "Id"> <generator class = "guid.comb"/> </id> <property name = "FirstName"/> <property name = "LastName"/> <property name = "AverageRating"/> <property name = "Points"/> <property name = "HasGoldStatus"/> <property name = "MemberSince" type = "UtcDateTime"/> <property name = "CreditRating" type = "CustomerCreditRatingType"/> <component name = "Address"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan" fetch = "join"> <key column = "CustomerId"/> <one-to-many class = "Order"/> </set> </class> </hibernate-mapping>
您可以看到我們沒有更改應用程式中的任何程式碼,我們只是在**customer.hbm.xml**中添加了一個獲取策略。讓我們再次執行此應用程式,它的行為仍然完全相同。讓我們看看NHibernate Profiler。

之前,程式對資料庫進行了兩次往返,現在只有一次,這是因為它在這裡執行了一個左外部連線。
我們可以看到它根據客戶ID在客戶表和訂單表之間執行了一個左外部連線,因此它能夠一次載入所有這些資訊。
我們節省了一次資料庫往返。
缺點是客戶資訊會在兩行上重複,這就是SQL左外部連線的工作方式。
因此,使用獲取策略,我們提取了更多資料,並節省了一次往返。
您也可以在查詢級別執行此操作,因此讓我們轉到**Program.cs**檔案並檢視簡化過載的示例。
using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { //var query = from customer in session.Query<Customer>() // select customer; //var reloaded = query.Fetch(x => x.Orders).ToList(); var reloaded = session.Load<Customer>(id); Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded); Console.WriteLine("The orders were ordered by: "); foreach (var order in reloaded.Orders) { Console.WriteLine(order.Customer); } tx.Commit(); }
在這裡,我們正在透過customer進行載入。現在讓我們將其更改為查詢,我們將使用如下程式碼所示的linq查詢。
using(var session = sessionFactory.OpenSession()) using(var tx = session.BeginTransaction()) { var query = from customer in session.Query<Customer>() where customer.Id == id select customer; var reloaded = query.Fetch(x => x.Orders).ToList().First(); Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded); tx.Commit(); }
讓我們也從**customer.hbm.xml**檔案中刪除獲取策略。
<?xml version = "1.0" encoding = "utf-8" ?> <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" namespace = "NHibernateDemo"> <class name = "Customer"> <id name = "Id"> <generator class = "guid.comb"/> </id> <property name = "FirstName"/> <property name = "LastName"/> <property name = "AverageRating"/> <property name = "Points"/> <property name = "HasGoldStatus"/> <property name = "MemberSince" type = "UtcDateTime"/> <property name = "CreditRating" type = "CustomerCreditRatingType"/> <component name = "Address"> <property name = "Street"/> <property name = "City"/> <property name = "Province"/> <property name = "Country"/> </component> <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"> <key column = "CustomerId"/> <one-to-many class = "Order"/> </set> </class> </hibernate-mapping>
讓我們再次執行此應用程式,您將看到以下輸出。
New Customer: John Doe (00000000-0000-0000-0000-000000000000) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Unspecified) CreditRating: Good AverageRating: 42.42424242 Orders: Order Id: 00000000-0000-0000-0000-000000000000 Order Id: 00000000-0000-0000-0000-000000000000 Reloaded: John Doe (6ebacd17-f9ba-4ad8-9817-a5bb01112a5a) Points: 100 HasGoldStatus: True MemberSince: 1/1/2012 12:00:00 AM (Utc) CreditRating: Good AverageRating: 42.4242 Orders: Order Id: 16a6596b-d56e-41c7-9681-a5bb01112a60 Order Id: d41d615b-0f21-4032-81db-a5bb01112a61 Press <ENTER> to exit...
現在讓我們看看NHibernate Profiler,您可以看到我們再次進行了這個渴望的join fetch,但是這次是基於查詢的。
