請注明轉載地址:http://www.cnblogs.com/arhat
通過上一章的學習,我們學會如何使用NHibernate對數據的簡單查詢,刪除,更新和插入,那么如果說僅僅是這樣的話,那么NHibenrate的優勢有在哪里呢?那么今天就要和大家一起來分享一下NHibernate的優勢——懶加載(初探)。
我們知道,在關系數據庫中,表和表之間是有聯系的,那么通常情況下,我們通過連接查詢能夠把相關的數據查詢處理,只是連接語句似乎大概寫起來似乎是有些繁瑣的,那么NHibenrate為我們提供了一種變相的操作,可以使這種連接查詢變得簡單多了。
現在呢,我們更改一下數據庫,在數據庫中我們新建一個表Clazz(班級表),並插入相關的數據。
同時,我們得更改一下Student表,在Student表中加入一個外鍵Cid,我們知道,學生和班級之間是存在者關系的。我們從兩個方面來說:
對於Clazz:一個班級對應多個屬性就是一對多的關系(one-to-many),是one的一方
對於Student:多個學生對應一個班級就是多對一的關系(mand-to-one),是many的一方
所以,我們需要對Student表中建立一個和Clazz對應的外鍵
好,現在我們清空一下Student中數據。
下面就是一個重點了,我們需要改寫一下Student實體類和添加一個Clazz實體類。由於Student是Clazz的外鍵表,所以,我們應該這樣改寫Student實體類,在Student實體類中添加一個屬性為Clazz,用來獲得這個學生對應的班級,代碼如下:
public class Student { public virtual int SId { get; set; } public virtual string SName { get; set; } public virtual string SSex { get;set; } public virtual DateTime SBirthday{get;set;} public virtual Clazz Clazz { get; set; } }
然后添加Clazz類,代碼如下
public class Clazz { public virtual int CId { get; set; } public virtual string CName { get; set; } public virtual ISet<Model.Student> Students { get; set; } }
由於,Clazz和Student是多對一的關系,所以在Clazz中需要定義一個集合屬性,用來獲得這個班級中的學生。這里使用的集合是Iesi.Collections.Generic.ISet類型。那么需要在Model項目中添加Iesi.Collections.dll的引用。
但是我們雖然更改了實體類的屬性,但是NHibernate卻不知道他們之間的關系,所以我們需要更改Student.hbm.xml和Clazz.hbm.xml映射文件,使NHibernate能夠知道他們之間的關系。
Student.hbm.xml內容如下:
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping assembly="Model" namespace="Model" xmlns="urn:nhibernate-mapping-2.2"> <class name="Student" table="Student"> <id name="SId" type="int"> <column name="sid"></column> <generator class="native"></generator> </id> <property name="SName" type="string"> <column name="sname"/> </property> <property name="SSex" type="string"> <column name="ssex"/> </property> <property name="SBirthday" type="DateTime"> <column name="sbirthday"/> </property> <many-to-one name="Clazz" column="CId" class="Model.Clazz"></many-to-one> </class> </hibernate-mapping>
在Student.hbm.xml中,我們添加了一個<many-to-one>的節點,這個節點是用來說明在Student中Clazz屬性是一個外鍵。其中name是Student中Clazz屬性的屬性名,column是Student表中的外鍵字段名,class是用來設置和Student關聯的對象的完整名字(命名空間+類名)。
然后,我們看一下Clazz.hbm.xml的文件內容。
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping assembly="Model" namespace="Model" xmlns="urn:nhibernate-mapping-2.2"> <class name="Clazz" table="clazz" lazy="true"> <id name="CId" type="int"> <column name="cid"></column> <generator class="native"></generator> </id> <property name="CName" type="string"> <column name="cname"></column> </property> <set name="Students" table="student"> <key column="Cid"></key> <one-to-many class="Model.Student"/> </set> </class> </hibernate-mapping>
大家可以看到,由於在Clazz實體類中我們定義了一個集合屬性,那么也就是表示了Clazz和Sutdent之間的多對一關系。那么自在Clazz.hbm.xml映射文件中,需要通過<set>節點來聲明這個屬性。其中<Set>節點中的name是Clazz中的集合屬性名,table是指對應着數據庫的那個表。其中<set>節點中有兩個子節點<key>是用來說明Student表中的外鍵字段,<ono-to-many>是用來說明班級和學生之間的一對多關系,class屬性是指和Clazz關聯的Student的全類名。
一旦這兩個映射文件通過<many-to-one>和<one-to-many>的設置,那么NHibenrate就知道了Student和Clazz之間的關系了。
由於在Student表中沒有數據,我們現在插入幾條數據來做測試。
然后,我們得做兩個測試,才能說明NHibernate給我提供的懶加載機制以及測試中的問題。現在我們更改一下D_User.cs的代碼
public Model.Student GetUser(int id) { //using(ISession session = NHibernateHelper.OpenSession()) //{ // return session.Get<Model.Student>(id); //} //} ISession session = NHibernateHelper.OpenSession(); { return session.Get<Model.Student>(id); } }
現在老魏有一個要求,就是要查找一下id=1學生的姓名和所在的班級名稱。那么如果在SQL中,我們得使用一個連接語句,但是在NHibernate中一切將會變得簡單多了。
然后,我們在主程序中更改一下代碼:
DAL.D_User dal = new DAL.D_User(); Model.Student student = dal.GetUser(1); Console.WriteLine("學生:"+student.SName+",所在的班級是:"+student.Clazz.CName);
運行一下,看看結果如何
我們發現,的確查詢出了正確的結果。那么在執行的時候,NHibernate發出了兩條SQL語句,那么大家可以看出,一個是查詢Student的語句,一個是查詢班級的SQL語句,那么現在我們的問題就來了,為什么NHibernate會發出兩條語句呢。我們來測試一下,我們把:
Console.WriteLine("學生:"+student.SName+",所在的班級是:"+student.Clazz.CName);
給更改一下,只輸出學生的姓名。我們會發現NHibernate只發出了一條SQL語句。
那么這是為什么呢?原來是NHibernate在做查詢的時候,只是把數據Student表的數據查詢出來了,反而和它關聯的Clazz數據並沒有查詢出來。但是如果我們把注釋去掉,在運行的時候,會發現NHibernate發出兩條語句,而第二條語句就是用來查詢和Student關聯的Clazz屬性的。這是為什么呢?這個原因就是在NHibernate默認情況下是啟用懶加載機制的,那么什么是懶加載呢?
懶加載,我們認為是在需要的時候才開始執行,不需要的時候就不執行。那上面我們的測試結果已經說明這個問題了,當我們只是查詢Student的基本信息時,它就只查詢Student信息,但是現在由於我們不緊要顯示Student的基本信息,還要顯示班級信息,那么在顯示完Student信息之后,發現有一句話:student.Clazz.CName。那么NHibenrate就會知道:”哦,現在你需要你的班級信息了,那好吧,我把班級信息查詢出來,並班Clazz的信息創建一個對象,把這個對象賦值給Student的Clazz屬性吧”。此時,NHibenrate就會向數據庫發送一條SQL語句來查詢Clazz的信息。
從上面我么可以看出,NHinberate的最大優點就是擁有的懶加載,使我們的查詢變得簡單起來了。
下面我們得做第二實驗,D_User代碼如下:
public Model.Student GetUser(int id) { using(ISession session = NHibernateHelper.OpenSession()) { return session.Get<Model.Student>(id); } } //ISession session = NHibernateHelper.OpenSession(); //{ // return session.Get<Model.Student>(id); //} }
這回呢,我們使用using語句來釋放ISession資源。主程序代碼不變,我們來看看運行的結果:
出錯了,報了一個異常”no Session”。這是為什么呢?因為我們使用using語句來強制釋放ISession的資源,而這個時候只能查詢出Student的基本信息,反而查詢不到了和它關聯的Clazz西信息,原因很簡單,就是懶加載的執行需要ISession在沒有釋放的前提下才能夠執行的,所以我們一旦釋放了ISession的資源,則懶加載是不起作用的。那么看到這里,大家可以想到,這是懶加載的問題,那么如果我們不使用懶加載不就可以了嗎?我們來做一下實驗。把Student.hbm.xml和Clazz.hbm.xml的<class>節點中取消懶加載,代碼如下:
<class name="Clazz" table="clazz" lazy="false"> <class name="Student" table="Student" lazy="false">
然后我們運行一下程序,看看結果如何:
的確查詢出來了,但是我么看看SQL語句,此時的SQL語句是一個連接查詢,當然從性能上看基本上沒有什么影響,因為這是從many這一段發起的。如果從one那一短發起,那就大大不同了,至於為什么,我們在下一章中來討論。
我們會發現,如果我們取消了懶加載,那么結果是正確的,這就叫“立即執行”。但是,如果沒有了懶加載的話,那么我們在寫程序的時候會非常的難受,因為我們感受到懶加載給我們帶來的好處,一般情況下,我們在使用完ISession之后要釋放資源的。所以這里就出現一個矛盾,我們既要釋放資源,也要使用懶加載,那么該怎么辦呢?請看下章!