多對多關系
廢話不多說,看圖:
這裡我們主要是要解決Order表和Product表之間的多對多的關系,相信有過資料庫設計經驗的人是很容易了解的,OrderProduct是一個關系表,而Order和Product表都是實體表,在資料庫中多對多的的實體關系都通過在兩個實體間增加一個關系表來解決,如下:
通過在NHibernate中配置Order和Product之間的關系,我們可以直接還原這兩個表之間本來的關系(比如:我們可以這樣通路order.products.count來擷取某個訂單下有多少個産品,或者product.orders.count來檢視某個産品有多少個訂單是定了的,這樣是不是很符合我們的思維,更有助于我們對業務的了解),而如果使用SQL的話,我們必須通過它們之間的關系表來擷取他們之間的關系,配置後這些都可以交由NHibernate來自動完成了。
一、編寫Product表對應的實體類和hbm映射檔案
Product.cs代碼如下:
namespace Model.Entities
{
public class Product
{
public virtual Int32 ProductId { get; set; }
public virtual string Name { get; set; }
public virtual decimal Cost { get; set; }
}
}
Product.hbm.xml代碼如下:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model.Entities">
<class name="Model.Entities.Product,Model" table="Product">
<id name="ProductId" column="ProductId" type="Int32" unsaved-value="0">
<generator class="native"/>
</id>
<property name="Name" column="Name" type="string" length="50" not-null="true"/>
<property name="Cost" column="Cost" type="decimal" not-null="true"/>
</class>
</hibernate-mapping>
二、修改Order表對應的實體類和hbm映射檔案
Order.cs修改如下:
namespace Model.Entities
{
public class Order
{
public virtual Int32 OrderId { get; set; }
public virtual DateTime OrderDate { get; set; }
//public virtual Int32 Customer { get; set; }
//一個order是屬于一個customer的
public virtual Customer Customer { get; set; }
//多對多一個Order可以擁有多個Products
public virtual IList<Product> Products { get; set; }
}
}
Order.hbm.xml修改如下:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model.Entities">
<!--注意:下面的table="[Order]"屬性,這是因為Order是sql中的關鍵字,如果不用[]括起來,将會在執行時報錯,大家在到時可以試試-->
<class name="Model.Entities.Order,Model" 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="false"></property>
<!--同樣請注釋掉Customer屬性-->
<!--<property name="Customer" column="Customer" type="Int32" not-null="false"></property>-->
<!--一個order屬于一個Customer-->
<many-to-one name="Customer" column="Customer" not-null="true" class="Model.Entities.Customer,Model" foreign-key="FK_CustomerOrders"/>
<!--多對多,一個Order有多個Products,這裡配置的意思是,OrderProduct表中一個key字段即[Order]字段對應多個Product-->
<bag name="Products" generic="true" table="OrderProduct"><!—name_對應Order.cs中的Products屬性、table_關系表-->
<key column="[Order]" foreign-key="FK_OrderProducts"/><!—column_OrderProduct表的字段_外鍵、foreign-key_關系名稱,FK_是必須的-->
<many-to-many column="Product" class="Model.Entities.Product,Model" foreign-key="FK_ProductOrders"/><!—column_OrderProduct表的字段_外鍵-->
</bag>
</class>
</hibernate-mapping>
三、修改Product表對應的實體類和hbm映射檔案
Product.cs修改如下:
namespace Model.Entities
{
public class Product
{
public virtual Int32 ProductId { get; set; }
public virtual string Name { get; set; }
public virtual decimal Cost { get; set; }
//多對多一個Product可以屬于多個Orders
public virtual IList<Order> Orders { get; set; }
}
}
Product.hbm.xml修改如下:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model.Entities">
<class name="Model.Entities.Product,Model" table="Product">
<id name="ProductId" column="ProductId" type="Int32" unsaved-value="0">
<generator class="native"/>
</id>
<property name="Name" column="Name" type="string" length="50" not-null="true"/>
<property name="Cost" column="Cost" type="decimal" not-null="true"/>
<!--多對多,解釋可以參考上面Order.hbm.xml的注釋-->
<bag name="Orders" generic="true" table="OrderProduct">
<key column="Product" foreign-key="FK_ProductOrders"/>
<many-to-many column="[Order]" class="Model.Entities.Order,Model" foreign-key="FK_OrderProducts"/>
</bag>
</class>
</hibernate-mapping>
四、為相關表添加關聯資料
資料可以由自己添加,這裡提供一個參考:
五、編寫DAL(資料通路層、持久層)方法來關聯查詢Customer、Order、OrderProduct和Product表
方法可以放在舊有的檔案裡面,也可以放在建立的檔案裡面,具體參考前面的DAL層的相關檔案,代碼如下:
//關聯查詢——多對多
public IList<Customer> GetCustomersWithOrdersAndProducts(DateTime orderDate)
{
IList<Customer> customers = null;
//用SQL查詢
//customers = session
// .CreateSQLQuery("select distinct customer.* from Customer customer inner join [Order] o on o.Customer=customer.CustomerId inner join OrderProduct op on o.OrderId=op.[Order] inner join Product p on op.Product=p.ProductId where o.OrderDate< :orderDate")
// .AddEntity("customer", typeof(Customer))
// .SetDateTime("orderDate", orderDate)
// .List<Customer>();
//用HQL查詢
customers = session
.CreateQuery("select distinct c from Customer c inner join c.Orders o where o.OrderDate<=:orderDate")
.SetDateTime("orderDate", orderDate)
.List<Customer>();
return customers;
}
注意:上面的方法中提供了兩種查詢方式,一種是SQL查詢、一種是HQL查詢,當你使用其中一種時,請注釋掉另外一種,以確定測試的準确性;
六、編寫DAL.Test項目中的測試方法
方法可以放在舊有的檔案裡面,也可以放在建立的檔案裡面,具體參考前面的DAL.Test層的相關檔案,代碼如下:
[Test]
public void GetCustomers()
{
IList<Customer> customers = sample.GetCustomersWithOrdersAndProducts(DateTime.Now);
}
在函數中設定一個斷點,然後在“即時視窗”輸入代碼如下:
要通路Customer的屬性就OK但是要通路Order的熟悉就隻能通路Count,其它的屬性不能通路。
七、小宇宙爆發——通路關聯對象及其屬性
上面的截圖我們可以看到,這裡無法通過customer關聯通路其order對象,這是因為我不會用ISet集合,好吧!既然我不會用就改用IList吧,不過要做小小的修改,如下:
Customer.cs修改如下:
//注意,實體類必須為public,否則無法通路
public class Customer
{
//在NHibernate的實體類中,所有的公共方法、屬性和事件都必須使用virtual修飾
public virtual int CustomerId { get; set; }
public virtual string Firstname { get; set; }
public virtual string Lastname { get; set; }
//一個Customer有多個Order
//public virtual ISet<Order> Orders { get; set; } //ISet在映射檔案中對應Set配置節
public virtual IList<Order> Orders { get; set; } //IList在映射檔案中對應bag配置節
}
Customer.hbm.xml修改如下:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model.Entities">
<class name="Model.Entities.Customer,Model" 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>
<property name="Lastname" column="Lastname" type="string" length="50" not-null="false"></property>
<!--一個Customer對應多個order-->
<!--<set name="Orders" table="[Order]" generic="true" inverse="true">
<key column="Customer" foreign-key="FK_CustomerOrders"/>
<one-to-many class="Model.Entities.Order,Model"/>
</set>-->
<bag name="Orders" table="[Order]" generic="true" inverse="true">
<key column="Customer" foreign-key="FK_CustomerOrders"/>
<one-to-many class="Model.Entities.Order,Model"/>
</bag>
</class>
</hibernate-mapping>
在函數GetCustomers()中設定一個斷點,然後在“即時視窗”輸入代碼如下:
從上面的代碼我們可以看到,我們可以随意的通路關聯對象及其屬性,hoho~~還是IList好用。