一步步學習NHibernate(4)——多對一,一對多,懶加載(1)


請注明轉載地址:http://www.cnblogs.com/arhat

通過上一章的學習,我們學會如何使用NHibernate對數據的簡單查詢,刪除,更新和插入,那么如果說僅僅是這樣的話,那么NHibenrate的優勢有在哪里呢?那么今天就要和大家一起來分享一下NHibernate的優勢——懶加載(初探)。

我們知道,在關系數據庫中,表和表之間是有聯系的,那么通常情況下,我們通過連接查詢能夠把相關的數據查詢處理,只是連接語句似乎大概寫起來似乎是有些繁瑣的,那么NHibenrate為我們提供了一種變相的操作,可以使這種連接查詢變得簡單多了。

現在呢,我們更改一下數據庫,在數據庫中我們新建一個表Clazz(班級表),並插入相關的數據。

wps_clip_image-560

同時,我們得更改一下Student表,在Student表中加入一個外鍵Cid,我們知道,學生和班級之間是存在者關系的。我們從兩個方面來說:

對於Clazz:一個班級對應多個屬性就是一對多的關系(one-to-many),是one的一方

對於Student:多個學生對應一個班級就是多對一的關系(mand-to-one),是many的一方

所以,我們需要對Student表中建立一個和Clazz對應的外鍵

wps_clip_image-4236

好,現在我們清空一下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表中沒有數據,我們現在插入幾條數據來做測試。

wps_clip_image-21467

然后,我們得做兩個測試,才能說明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);

運行一下,看看結果如何

wps_clip_image-14777

我們發現,的確查詢出了正確的結果。那么在執行的時候,NHibernate發出了兩條SQL語句,那么大家可以看出,一個是查詢Student的語句,一個是查詢班級的SQL語句,那么現在我們的問題就來了,為什么NHibernate會發出兩條語句呢。我們來測試一下,我們把:

Console.WriteLine("學生:"+student.SName+",所在的班級是:"+student.Clazz.CName);

給更改一下,只輸出學生的姓名。我們會發現NHibernate只發出了一條SQL語句。

wps_clip_image-18415

那么這是為什么呢?原來是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資源。主程序代碼不變,我們來看看運行的結果:

wps_clip_image-12360

出錯了,報了一個異常”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">

然后我們運行一下程序,看看結果如何:

wps_clip_image-1790

的確查詢出來了,但是我么看看SQL語句,此時的SQL語句是一個連接查詢,當然從性能上看基本上沒有什么影響,因為這是從many這一段發起的。如果從one那一短發起,那就大大不同了,至於為什么,我們在下一章中來討論。

我們會發現,如果我們取消了懶加載,那么結果是正確的,這就叫“立即執行”。但是,如果沒有了懶加載的話,那么我們在寫程序的時候會非常的難受,因為我們感受到懶加載給我們帶來的好處,一般情況下,我們在使用完ISession之后要釋放資源的。所以這里就出現一個矛盾,我們既要釋放資源,也要使用懶加載,那么該怎么辦呢?請看下章!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM