程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> NHibernate之旅(9):探索父子關系(一對多關系)

NHibernate之旅(9):探索父子關系(一對多關系)

編輯:關於.NET

本節內容

引入

NHibernate中的集合類型

建立父子關系

父子關聯映射

結語

引入

通過前幾篇文章的介紹,基本上了解了NHibernate,但是在NHibernate中映射關系是NHibernate中的亮點,也是最難掌握的技術。從這篇開始學習這些東西,我將圖文結合來說明這裡奧秘的知識。

前幾篇,我們的例子只使用了一個簡單的Customer對象。但是在客戶/訂單/產品的經典組合中,他們的關系非常復雜?讓我們先回顧在第二篇中建立的數據模型。

在圖上,我已經清晰的標注了表之間的關系,首先分析Customer和Order之間的“外鍵關系”或者稱作“父子關系”、“一對多關系”。在分析之前先初步了解NHibernate中的集合。

NHibernate中的集合類型

NHibernate支持/定義的幾種類型的集合:

Bag:對象集合,每個元素可以重復。例如{1,2,2,6,0,0},在.Net中相當於IList或者IList<T>實現。

Set:對象集合,每個元素必須唯一。例如{1,2,5,6},在.Net中相當於ISet或者ISet<T>實現,Iesi.Collections.dll程序集提供ISet集合。

List:整數索引對象集合,每個元素可以重復。例如{{1,"YJingLee"},{2,"CnBlogs"},{3,"LiYongJing"}},在.Net中相當於ArraryList或者List<T>實現。

Map:鍵值對集合。例如{{"YJingLee",5},{"CnBlogs",7},{"LiYongJing",6}},在.Net中相當於HashTable或者IDictionary<Tkey,TValue>實現。

實際上,我們大多數情況下使用Set集合類型,因為這個類型和關系型數據庫模型比較接近。

建立父子關系

直接看下面一幅圖的兩張表:

上面兩張表關系表達的意思是:Customer有一個或多個Orders,Orders屬於一個Customer。一般而言,我們稱Customer為“父”,Order稱為“子”。Customer和Order之間關系就有幾種說法:“外鍵關系”、“父子關系”、“一對多關系”都可以。

1.Customer有一個或多個Orders

在對象模型中:在Customer類中把Orders作為一個集合,這時可以說Customer對象包含了Orders集合。在.NET中通常這樣表述:

public class Customer
{
  //......
  public IList<Order> Orders{ get; set; }
}

訪問對象方式:通過子集合訪問:Customer.Orders[...]

在NHibernate中,通常而言使用Iesi.Collections.dll程序集中的ISet集合,現在修改Customer.cs類,首先需要引用這個程序集,Customer.cs類代碼如下:

using Iesi.Collections.Generic;
namespace DomainModel.Entities
{
  public class Customer
  {
    public virtual int CustomerId { get; set; }
    public virtual string Firstname { get; set; }
    public virtual string Lastname { get; set; }
    //一對多關系:Customer有一個或多個Orders
    public virtual ISet<Order> Orders { get; set; }
  }
}

2.Order屬於一個Customer

在對象模型中:在Order類中把Customer作為單一對象,這時可以說Order對象包含了一個Customer。在.NET中通常這樣表述:

public class Order
{
  //......
  public Customer Customer{ get; set; }
}

其訪問對象方式:通過父對象成員訪問:Order.Customer

我們在項目DomainModel層的Entities文件夾中新建Order.cs類,編寫代碼如下:

namespace DomainModel.Entities
{
  public class Order
  {
    public virtual int OrderId { get; set; }
    public virtual DateTime OrderDate { get; set; }
    //多對一關系:Orders屬於一個Customer
    public virtual Customer Customer { get; set; }
  }
}

好了,我們現在完成持久類了,下面看看這兩個類如何映射。

父子關聯映射

在NHibernate中,我們可以通過映射文件來關聯對象之間的關系。映射文件定義了:

對象之間關系:一對一、一對多、多對一、多對多關系。

在關系中控制級聯行為(Cascade behavior):級聯更新、級聯刪除

父子之間的雙向導航(bidirectional navigation)

1.父實體映射

父實體(Customer)映射定義了:

集合類型(Bag、Set、List、Map)

在保存、更新、刪除操作時的級聯行為

關聯的控制方向:

Inverse="false"(默認):父實體負責維護關聯關系

Inverse="true":子實體負責維護關聯關系

與子實體關聯的關系(一對多、多對一、多對多)

這些具體的設置是NHibernate中的難點所在,以後慢慢討論這些不同設置下的奧秘之處。

這一篇初步建立Customer與Order的一對多關系,修改Customer.hbm.xml映射文件如下:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
          assembly="DomainModel" namespace="DomainModel">
 <class name ="DomainModel.Entities.Customer,DomainModel"
     table="Customer">
  <id name="CustomerId" column="CustomerId" type="Int32"
    unsaved-value="0">
   <generator class ="native"></generator>
  </id>
  <property name="Firstname" column ="Firstname" type="string"
       length="50" not-null="false"/>
  <property name ="Lastname" column="Lastname" type="string"
       length="50" not-null="false"/>
  <!--一對多關系:Customer有一個或多個Orders-->
  <set name="Orders" table="`Order`" generic="true" inverse="true">
     <key column="Customer" foreign-key="FK_CustomerOrders"/>
     <one-to-many class="DomainModel.Entities.Order,DomainModel"/>
  </set>
 </class>
</hibernate-mapping>

可以看到,在“父”端映射使用Set元素,標明屬性名稱、表名、子實體負責維護關聯關系。

2.子實體映射

子實體(Order)映射定義的東西就是父實體少了:與父實體關聯的(多對一、一對多、多對多) 關系,並用一個指針來導航到父實體。

在“子”端通過many-to-one元素定義與“父”端的關聯,從“子”端角度看這種關系模型是多對一關聯(實際上是對Customer對象的引用)。下面看看many-to-one元素映射屬性:

看看這些映射屬性具體有什麼意義:

access(默認property):可選field、property、nosetter、ClassName值。NHibernate訪問屬性的策略。

cascade(可選):指明哪些操作會從父對象級聯到關聯的對象。可選all、save-update、delete、none值。除none之外其它將使指定的操作延伸到關聯的(子)對象。

class(默認通過反射得到屬性類型):關聯類的名字。

column(默認屬性名):列名。

fetch(默認select):可選select和join值,select:用單獨的查詢抓取關聯;join:總是用外連接抓取關聯。

foreign-key:外鍵名稱,使用SchemaExport工具生成的名稱。

index:......

update,insert(默認true):指定對應的字段是否包含在用於UPDATE或INSERT 的SQL語句中。如果二者都是false,則這是一個純粹的 “外源性(derived)”關聯,它的值是通過映射到同一個(或多個)字段的某些其他特性得到或者通過觸發器其他程序得到。

lazy:可選false和proxy值。是否延遲,不延遲還是使用代理延遲。

name:屬性名稱propertyName。

not-found:可選ignore和exception值。找不到忽略或者拋出異常。

not-null:可選true和false值。

outer-join:可選auto、true、false值。

property-ref(可選):指定關聯類的一個屬性名稱,這個屬性會和外鍵相對應。如果沒有指定,會使用對方關聯類的主鍵。這個屬性通常在遺留的數據庫系統使用,可能有外鍵指向對方關聯表的某個非主鍵字段(但是應該是一個唯一關鍵字)的情況下,是非常不好的關系模型。比如說,假設Customer類有唯一的CustomerId,它並不是主鍵。這一點在NHibernate源碼中有了充分的體驗。

unique:可選true和false值。控制NHibernate通過SchemaExport工具生成DDL的過程。

unique-key(可選):使用DDL為外鍵字段生成一個唯一約束。

我們來建立“子”端到“父”端的映射,新建Order.hbm.xml文件,編寫代碼如下:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
          assembly="DomainModel" namespace="DomainModel">

 <class name="DomainModel.Entities.Order,DomainModel" table="`Order`" >
  <id name="OrderId" column="OrderId" type="Int32" unsaved-value="0">
   <generator class="native" />
  </id>
  <property name="OrderDate" column="OrderDate" type="DateTime"
       not-null="true" />
  <!--多對一關系:Orders屬於一個Customer-->
  <many-to-one name="Customer" column="Customer" not-null="true"
         class="DomainModel.Entities.Customer,DomainModel"
         foreign-key="FK_CustomerOrders" />
 </class>
</hibernate-mapping>

關於如何關聯看看上面的屬性就一目了然了。

結語

這一篇建立Customer和Order之間一對多關聯關系,並初步了解NHibernate中的集合。下一篇繼續以這一篇為基礎,一步一步去挖掘,這是我一貫寫法,介紹在NHibernate中使用原生SQL、HQL、Criteria API三種查詢方式來關聯查詢。當然了,從這篇開始,研究的東西就更多了,像查詢、加載機制、代理機制、再議並發控制、NHibernate提供的有用類等等。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved