目錄
寫在前面
系列文章
一對多關系
總結
上篇文章簡單介紹了,Fluent Nhibernate使用代碼的方式生成Nhibernate的配置檔案,以及如何生成持久化類的映射檔案。通過上篇的學習你會發現,Fluent Nhibernate仍然需要引用Nhibernate的兩個程式集(Nhibernate.dll和Iesi.Collections.dll),是以與Nhibernate最大的差別就在生成配置檔案的方式上面,這裡關于Nhibernate的特性方面就不再多贅述,可以參考Nhibernate相關的文章。這裡主要研究一下Nhibernate中需在配置檔案中進行配置的一些特性。那麼就先提一下一對多關系的處理。
測試用的資料庫仍然采用學習Nhibernate時,使用的資料庫。
耗時兩月,NHibernate系列出爐
[Fluent NHibernate]第一個程式
這裡将資料庫表及關系圖貼出,方面檢視:
這裡就使用一個客戶可以對應多個訂單的一對多關系進行分析。
首先在持久化類中添加一對多關系的處理
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using NHibernate;
7 using FluentNHibernate;
8 namespace Wolfy.Domain.Entities
9 {
10 /// <summary>
11 /// 描述:客戶實體,資料庫持久化類
12 /// 建立人:wolfy
13 /// 建立時間:2014-10-16
14 /// </summary>
15 public class Customer
16 {
17 /// <summary>
18 /// 客戶id
19 /// </summary>
20 public virtual Guid CustomerID { get; set; }
21 /// <summary>
22 /// 客戶名字
23 /// </summary>
24 public virtual string CustomerName { get; set; }
25 /// <summary>
26 /// 位址
27 /// </summary>
28 public virtual string CustomerAddress { set; get; }
29 /// <summary>
30 /// 版本控制
31 /// </summary>
32 public virtual int Version { get; set; }
33 /// <summary>
34 /// 一對多關系:一個Customer有一個或者多個Order
35 /// </summary>
36 public virtual System.Collections.Generic.ISet<Order> Orders { set; get; }
37 }
38 }
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 namespace Wolfy.Domain.Entities
8 { /// <summary>
9 /// 描述:訂單實體,資料庫持久化類
10 /// 建立人:wolfy
11 /// 建立時間:2014-10-16
12 /// </summary>
13 public class Order
14 {
15 /// <summary>
16 /// 訂單id
17 /// </summary>
18 public virtual Guid OrderID { set; get; }
19 /// <summary>
20 /// 下訂單時間
21 /// </summary>
22 public virtual DateTime OrderDate { set; get; }
23 /// <summary>
24 /// 下訂單的客戶,多對一的關系:orders對應一個客戶
25 /// </summary>
26 public virtual Customer Customer { set; get; }
27 }
28 }
在Nhibernate中處理一對多關系的映射檔案為:
Customer.hbm.xml
1 <?xml version="1.0" encoding="utf-8" ?>
2 <!--assembly:程式集,namespace:命名空間-->
3 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain" namespace="Wolfy.Shop.Domain.Entities">
4 <!--存儲過程-->
5 <class name="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain" table="TB_Customer">
6 <!--二級緩存-->
7 <cache usage="read-write"/>
8 <!--主鍵-->
9 <id name="CustomerID" type="Guid" unsaved-value="null">
10 <column name="CustomerID" sql-type="uniqueidentifier" not-null="true" unique="true" />
11 <generator class="assigned"></generator>
12 </id>
13 <!--版本控制-->
14 <version name="Version" column="Version" type="integer" unsaved-value="0"/>
15 <!--一對多關系:一個客戶可以有一個或者多個訂單-->
16 <!--子實體負責維護關聯關系-->
17 <set name="Orders" table="TB_Order" generic="true" inverse="true" cascade="all">
18 <key column="CustomerID" foreign-key="FK_TB_Order_TB_Customer"></key>
19 <one-to-many class="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain"/>
20 </set>
21 </class>
22 </hibernate-mapping>
Order.hbm.xml
1 <?xml version="1.0" encoding="utf-8" ?>
2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain" namespace="Wolfy.Shop.Domain.Entities">
3 <class name="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain" table="TB_Order">
4 <id name="OrderID" column="OrderID" type="Guid" unsaved-value="null">
5 <generator class="assigned" />
6 </id>
7 <property name="OrderDate" column="OrderDate" type="DateTime"
8 not-null="true" />
9 <!--多對一關系:Orders屬于一個Customer-->
10 <many-to-one name="Customer" column="CustomerID" not-null="true"
11 class="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain"
12 foreign-key="FK_TB_Order_TB_Customer" />
13 </class>
14 </hibernate-mapping>
那麼Fluent Nhibernate對于一對多關系是如何處理的呢?
添加Order的映射類
1 using FluentNHibernate.Mapping;
2 using FluentNHibernate;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Text;
7 using System.Threading.Tasks;
8 using Wolfy.Domain.Entities;
9 namespace Wolfy.Domain.Mapping
10 {
11 /// <summary>
12 /// 描述:訂單實體映射類
13 /// 建立人:wolfy
14 /// 建立時間:2014-12-07
15 /// </summary>
16 public class OrderMapping : ClassMap<Order>
17 {
18 public OrderMapping()
19 {
20 //指定對應的資料表
21 Table("TB_Order");
22 //指定id主鍵
23 Id<Guid>("OrderID").GeneratedBy.Guid();
24 //映射其他的字段
25 Map(m => m.OrderDate).Nullable();
26 //處理多對一關系,多個order可以屬于一個customer
27 References<Customer>(r => r.Customer).Column("CustomerID").ForeignKey("CustomerID").Cascade.All();
28 }
29 }
30 }
在Order映射類中處理多對一關系使用References,更符合面向對象的概念,在實體類中的關系就這種引用的關系。
修改Customer映射類
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using NHibernate;
7 using FluentNHibernate.Mapping;
8 using Wolfy.Domain.Entities;
9 namespace Wolfy.Domain.Mapping
10 {
11 /// <summary>
12 /// Customer映射實體類,需要內建ClassMap泛型類
13 /// </summary>
14 public class CustomerMapping : ClassMap<Customer>
15 {
16 /// <summary>
17 /// 映射關系實體類的構造函數
18 /// 在構造函數中處理好映射關系
19 /// </summary>
20 public CustomerMapping()
21 {
22 //指定持久化類對應的資料表
23 Table("TB_Customer");
24 //自動增長的id
25 //Id(i => i.CustomerID);
26 //映射關系
27 Id<Guid>("CustomerID").GeneratedBy.Guid();
28 Map(m => m.CustomerAddress).Length(50).Nullable();
29 Map(m => m.CustomerName).Length(32).Nullable();
30 Map(m => m.Version);
31 //處理一對多關系的映射,一個客戶可以有多個訂單
32 //關聯的資料表進行懶加載,主鍵名為CustomerID,級聯關系所有操作,cascade:All|delete|saveorUpdate
33 HasMany<Order>(h => h.Orders).LazyLoad().AsSet().KeyColumn("CustomerID").Cascade.All();
34 }
35 }
36 }
單元測試
描述:建立一個客戶對象,并向客戶的訂單集合中添加兩個訂單,并斷言結果為true,即添加成功。
1 [TestMethod]
2 public void AddCustomerTest2()
3 {
4 var customer = new Customer()
5 {
6 Version = 1,
7 CustomerName = "wolfy",
8 CustomerAddress = "中國 北京",
9 CustomerID = Guid.NewGuid()
10 };
11 customer.Orders = new HashedSet<Order>();
12 customer.Orders.Add(new Order() { Customer = customer, OrderDate = DateTime.Now, OrderID = Guid.NewGuid() });
13 customer.Orders.Add(new Order() { Customer = customer, OrderDate = DateTime.Now, OrderID = Guid.NewGuid() });
14 var result = _customerData.AddCustomer(customer);
15 Assert.IsTrue(result);
16 }
運作測試
生成的sql語句
跟使用配置檔案的對比可參考我這篇文章:http://www.cnblogs.com/wolf-sun/p/4068749.html
那麼看一下在c盤生成的xml檔案:
xml檔案内容為:
1 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
2 <class xmlns="urn:nhibernate-mapping-2.2" name="Wolfy.Domain.Entities.Customer, Wolfy.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="TB_Customer">
3 <id type="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
4 <column name="CustomerID" />
5 <generator class="guid" />
6 </id>
7 <set cascade="all" lazy="true" name="Orders">
8 <key>
9 <column name="CustomerID" />
10 </key>
11 <one-to-many class="Wolfy.Domain.Entities.Order, Wolfy.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
12 </set>
13 <property name="CustomerAddress" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
14 <column name="CustomerAddress" length="50" not-null="false" />
15 </property>
16 <property name="CustomerName" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
17 <column name="CustomerName" length="32" not-null="false" />
18 </property>
19 <property name="Version" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
20 <column name="Version" />
21 </property>
22 </class>
23 </hibernate-mapping>
1 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
2 <class xmlns="urn:nhibernate-mapping-2.2" name="Wolfy.Domain.Entities.Order, Wolfy.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="TB_Order">
3 <id type="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
4 <column name="OrderID" />
5 <generator class="guid" />
6 </id>
7 <property name="OrderDate" type="System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
8 <column name="OrderDate" not-null="false" />
9 </property>
10 <many-to-one cascade="all" class="Wolfy.Domain.Entities.Customer, Wolfy.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" foreign-key="CustomerID" name="Customer">
11 <column name="CustomerID" />
12 </many-to-one>
13 </class>
14 </hibernate-mapping>
通過與nhibernate手寫的xml映射檔案,在結構上,内容上大概相似。
級聯查詢
查詢客戶資訊時,将查詢客戶下的所有的訂單
在CustomerData層添加如下方法并進行測試
1 /// <summary>
2 /// 獲得客戶資訊
3 /// </summary>
4 /// <param name="customerID"></param>
5 /// <returns></returns>
6 public Customer GetCustomer(Guid customerID)
7 {
8 ISession session = FluentNHibernateHelper.GetSession();
9 return session.Load<Customer>(customerID);
10 }
測試,并運作
1 [TestMethod]
2 public void GetCustomerTest()
3 {
4 var customer = _customerData.GetCustomer(new Guid("EB99D91A-F504-4BB4-8E52-EA10C96E6637"));
5 Assert.IsNotNull(customer);
6 Console.WriteLine("該客戶的訂單數量:" + customer.Orders.Count);
7 }
測試結果
這先查詢TB_Customer表,然後需要用到Order的數量,是以又查詢了TB_Order表。回頭看看我們在Customer的映射類中指定了使用懶加載的方式
1 HasMany<Order>(h => h.Orders).LazyLoad().AsSet().KeyColumn("CustomerID").Cascade.All();
測試是否是懶加載?
1 [TestMethod]
2 public void GetCustomerTest()
3 {
4 var customer = _customerData.GetCustomer(new Guid("EB99D91A-F504-4BB4-8E52-EA10C96E6637"));
5 Assert.IsNotNull(customer);
6 Console.WriteLine("客戶姓名:"+customer.CustomerName);
7 // Console.WriteLine("該客戶的訂單數量:" + customer.Orders.Count);
8 }
在這個測試裡面,我們并沒有用到Order,是以就查詢customer的資訊,需要的時候再去查詢Order表。在使用時發現,如果不輸出CustomerName,連查詢TB_Customer表的sql也不生成,充分說明,Nhibernate中的懶加載真夠懶的。
在測試的時候,順手把customer的屬性都輸出了,發現一個自認為不科學的地方:
發現了吧,很奇怪,對比一下生成的映射檔案,和手寫的Nhibernate的配置檔案,将Customer和Order的映射類的id生成規則修改為:
1 Id<Guid>("CustomerID").GeneratedBy.Assigned();
Fluent Nhibernate的customer映射檔案
Nhibernate手寫的配置檔案
1 <id name="CustomerID" type="Guid" unsaved-value="null">
2 <column name="CustomerID" sql-type="uniqueidentifier" not-null="true" unique="true" />
3 <generator class="assigned"></generator>
4 </id>
你會發現是何其的相似啊,為什麼查詢到Customer對象能拿到CustomerName卻拿不到id呢?
看看Assigned的注釋:
1 // 摘要:
2 // lets the application to assign an identifier to the object before Save()
3 // is called.
4 public TParent Assigned();
大概意思就是:讓應用程式在儲存方法被調用前為對象指定一個辨別符。
關于guid類型作為主鍵的政策,為什麼會傳回Guid(0),可以參考這篇文章
http://nhforge.org/blogs/nhibernate/archive/2009/05/21/using-the-guid-comb-identifier-strategy.aspx
如果在映射類中設定主鍵的生成政策為:
1 Id<Guid>("CustomerID").GeneratedBy.GuidComb();
在添加資料的時候,可以不用指定CustomerID=Guid.NewGuid();就好像是自增的int類型的主鍵一樣。
那怎麼才能拿到這個id呢?手動寫的配置檔案确實是可以拿到id的,而Fluent Nhibernate不可以。
找了很久也沒找到解決的辦法,突然那麼靈機一動,既然你傳回的是一個0串,也就是沒有指派的情況,那麼何不指定你的映射關系呢?
如下:
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using NHibernate;
7 using FluentNHibernate.Mapping;
8 using Wolfy.Domain.Entities;
9 namespace Wolfy.Domain.Mapping
10 {
11 /// <summary>
12 /// Customer映射實體類,需要內建ClassMap泛型類
13 /// </summary>
14 public class CustomerMapping : ClassMap<Customer>
15 {
16 /// <summary>
17 /// 映射關系實體類的構造函數
18 /// 在構造函數中處理好映射關系
19 /// </summary>
20 public CustomerMapping()
21 {
22 //指定持久化類對應的資料表
23 Table("TB_Customer");
24 //自動增長的id
25 //Id(i => i.CustomerID);
26 //映射關系
27 Id<Guid>("CustomerID").GeneratedBy.GuidComb();
28 //指定主鍵後一定要加上主鍵字段的映射關系,不然傳回的id為new Guid(),也就是一串0
29 Map(m => m.CustomerID).Nullable();
30 Map(m => m.CustomerAddress).Length(50).Nullable();
31 Map(m => m.CustomerName).Length(32).Nullable();
32 Map(m => m.Version);
33 //處理一對多關系的映射,一個客戶可以有多個訂單
34 //關聯的資料表進行懶加載,主鍵名為CustomerID,級聯關系所有操作,cascade:All|delete|saveorUpdate,級聯删除時需加上Inverse()
35 // Inverse the ownership of this entity. Make the other side of the relationship
36 // responsible for saving.
37 HasMany<Order>(h => h.Orders).LazyLoad().AsSet().KeyColumn("CustomerID").Cascade.All().Inverse();
38 }
39 }
40 }
從上面的代碼,可以看出指定id後,又指定了CustomerID的映射關系。這樣就可以拿到id了。對于從nhibernate過來的,估計已經思維定勢了,覺得指定id就已經指定了id的映射關系了,确實沒必要再去映射了。
問題到此解決。
級聯删除
級聯删除的時候,需指定Cascade和Inverse。具體測試可參考Nhibernate中一對多關系級聯删除内容。
1 /// <summary>
2 /// 删除指定客戶
3 /// </summary>
4 /// <param name="customer"></param>
5 /// <returns></returns>
6 public bool DeleteCustomer(Customer customer)
7 {
8 ISession session = FluentNHibernateHelper.GetSession();
9 using (var trans = session.BeginTransaction())
10 {
11 try
12 {
13 session.Delete(customer);
14 session.Flush();
15 trans.Commit();
16 return true;
17 }
18 catch (Exception)
19 {
20 trans.Rollback();
21 return false;
22 }
23 }
24 }
1 [TestMethod]
2 public void DeleteCustomerTest()
3 {
4 //得到删除的對象
5 Console.WriteLine("查詢要删除的customer對象");
6 var customer = _customerData.GetCustomer(new Guid("0D9862A6-D6FE-4861-BA61-A3FA00DC328C"));
7 Assert.IsNotNull(customer);
8 Console.WriteLine("将查詢到的對象删除");
9 var result = _customerData.DeleteCustomer(customer);
10 Assert.IsTrue(result);
11 }
本文涉及到級聯删除,添加,查詢以及一對多關系的處理等内容。還是那句話,與Nhibernate的差別就是在生成配置檔案及持久化類的映射檔案的方式上。關于Fluent Nhibernate的其他的操作也是在映射檔案上與Nhibernate設定上的差別(個人認為)。這裡就不再進行介紹了。收集了一些這方面的文章,供大家學習,确實在目前的項目中沒有用到,這東西,現在學了,也記不住,不經實踐,很難掌握。
[原創]Fluent NHibernate之旅(三)-- 繼承
[原創]Fluent NHibernate之旅
[原創]Fluent NHibernate之旅二--Entity Mapping
Fluent NHibernate RC 1.0 --更新内容
[原創]Fluent NHibernate之旅(四)-- 關系(上)
[原創]Fluent NHibernate之旅(四)-- 關系(中)
[原創]Fluent NHibernate之旅(四)-- 關系(下)
-
部落格位址:http://www.cnblogs.com/wolf-sun/
部落格版權:如果文中有不妥或者錯誤的地方還望高手的你指出,以免誤人子弟。如果覺得本文對你有所幫助不如【推薦】一下!如果你有更好的建議,不如留言一起讨論,共同進步!
再次感謝您耐心的讀完本篇文章。