Fluent NHibernate关系映射
1.好處:Fluent NHibernate讓你不再需要去寫NHibernate的標準映射文件(.hbm.xml), 方便了我們的代碼重構,提供了代碼的易讀性,并精簡了項目代碼
實現:
(1)、首先我們通過nuget包管理器添加FluentNHibernate與NHibernate的引用。
(2)、編寫實體類,注意實體的屬性都是virtual
(3)、實體類編寫完成以后我們用代碼的方式實現對實體的映射
Fluent NHibernate主要實現的映射關系:一對一、一對多、多對一、多對多
下面介紹幾種映射關系中主講一對多關聯屬性
?
2. 一對一映射 HasOne<>()
2.1. 定義支持的關聯屬性
Cascade 指該對象在進行操作時關聯到的子對象的操作類型
PropertyRef屬性一般用來解決遺留數據庫一對多關系的問題
PropertyRef被關聯到此外鍵的類中的對應屬性的名字,若沒指定,使用被關聯類的主鍵.
PropertyRef不是數據庫表中的字段名,而是定義的類中的屬性名
Fetch??? 在外連接抓取或者序列選擇抓取選擇其一.
class:被關聯的類的名字
constrained ?表明該類對應的表對應的數據庫表,和被關聯的對象所對應的數據庫表之間,通過一個外鍵引用對主鍵進行約束。 這個選項影響Save()和Delete()在級聯執行時的先后順序以及決定該關聯能否被委托
2.1.1.映射范例
用戶和用戶信息表在實際中是一對一的關系,這兩個表之間是通過使用UserID來相互關聯的,UserDetail使用的主鍵ID與User的ID是一致的,所以我們要使用Foregin來獲取User的ID,它們有一個共同的ID,在插入Users表的同時也要寫入UserDetail表,所以需要添加一對一的限制關系,具體的在User和UsersDetai兩表的映射方法如下代碼
public UserMap()
{
???????? Table("Users");?
Id(u => u.UserID).GeneratedBy.Identity() ;
Map(u => u.UserName);
Map(u => u.Password);
Map(u => u.CreateTime);
HasOne<UserDetail>(u => u.Detail).Cascade.All().PropertyRef("User");
}
public UserDetailMap()
{
Table("UserDetais");?
Id(u => u.UserID).Column("UserID").GeneratedBy.Foreign("User");
HasOne<User>(d => d.User).Cascade.All().Constrained();
Map(u => u.LastUpdated).Nullable();
Component<PersonName>(u => u.Name, p =>
{
p.Map(o => o.FirstName).Column("[First Name]");
p.Map(o => o.LastName).Column("[Last Name]");
});
}
2.1.2.補充說明
上面指定了All說明所有的操作都會關聯到子表,還有SaveUpdate在添加和更新時關聯子表,另外還有None類型不推薦使用此類型因為會出現很多問題。
一對一延遲加載
有時對我們來說,只需要User就可以了,我不需要查詢UserDetail,或許你會說,使用以下方式來進行延遲加載:
HasOne<UserDetail>(u => u.Detail).Cascade.All().LazyLoad();
雖然Fluent支持,雖然編譯通過,但在創建ISessionFactory的時候,卻會拋出異常,因為NHibernate不支持HasOne的不支持一對一的延遲Lazy加載的特性, 可以用:
HasOne<UserDetail>(u => u.Detail).Cascade.All().Fetch.Select();
HasOne<User>(d => d.User).Cascade.All().Constrained();
?
3. 一對多HasMany<>() / 多對一 References<>()
3.1. 定義支持的關聯屬性
KeyColumn? 表示主鍵名
Cascade表示級聯取值,決定是否把對對象的改動反映到數據庫中,所以Cascade對所有的關聯關系都起作用, Cascade是操作上的連鎖反映,Cascade取值可能是以下:
AllDeleteOrphan?????? 在關聯對象失去宿主(解除父子關系)時,自動刪除不屬于父對象的子對象, 也支持級聯刪除和級聯保存更新.
DeleteOrphan?????????? 刪除所有和當前對象解除關聯關系的對象????????
All???????????????? 級聯刪除, 級聯更新,但解除父子關系時不會自動刪除子對象.
Delete????????????????????????????????? 級聯刪除, 但不具備級聯保存和更新
None???????????????????????????????????? 所有操作均不進行級聯操作
SaveUpdate??????????????????????? 級聯保存(load以后如果子對象發生了更新,也會級聯更新). 在執行save/update/saveOrUpdate時進行關聯操作,它不會級聯刪除
Inverse() ???所描述的是對象之間關聯關系的維護方式,作用是:是否將對集合對象的修改反映到數據庫中。Inverse表示對集合對象的修改會被反映到數據庫中;
為了維持兩個實體類(表)的關系,而添加的一些屬性,該屬性可能在兩個實體類(表)或者在一個獨立的表里面,這個要看這雙方直接的對應關系了: 這里的維護指的是當主控方進行增刪改查操作時,會同時對關聯關系進行對應的更新,Inverse是操作上的連鎖反映。
ForeignKeyConstraintName?? 外鍵約束名
Access?? NHibernate用來訪問屬性的策略。
unique: 為外鍵字段生成一個唯一約束。此外, 這也可以用作PropertyRef的目標屬性。這使關聯同時具有 一對一的效果。
OptimisticLock? 定這個屬性在做更新時是否需要獲得樂觀鎖定(OptimisticLock)。 換句話說,它決定這個屬性發生臟數據時版本(version)的值是否增長。
NotFound?? 指定外鍵引用的數據不存在時如何處理: ignore會將數據不存在作為關聯到一個空對象(null)處理。
IsProxy???? 指定一個類或者接口,在延遲裝載時作為代理使用。
Schema
3.1.1. 映射范例
一個用戶可以擁有多個訂單,一個訂單只能擁有一個用戶,對于用戶來說,不需要每次都加載訂單列表,反之訂單可能每次都需要加載用戶信息。
public UserMap()
{
???????? Table("Users");?
Id(u => u.UserID).GeneratedBy.Identity() ;
Map(u => u.UserName);
Map(u => u.Password);
Map(u => u.CreateTime);
HasOne<UserDetail>(u => u.Detail).Cascade.All().Fetch.Select(); //一對一映射用戶和用戶信息表
HasMany<Order>(u => u.Orders).AsSet().KeyColumn("UserID").Cascade.All(); 處理一對多關系的映射,一個User可以有多個訂單, 關聯的數據表進行懶加載,主鍵名為UserID,級聯關系所有操作
}
public OrderMap()
{
?????????????????? Table("Orders");?
Id(o => o.OrderID).GeneratedBy.Identity();
Map(o => o.Price);
Map(o => o.State).CustomType<OrderState>();
Map(o => o.Address);
Map(o => o.Coignee);
Map(o => o.CreateTime);
Map(o => o.Zip);
References<User>(o => o.User).Not.LazyLoad().Column("UserID"); //處理多對一關系,多個Order可以屬于一個User
}
?
/// <summary>
??????? /// 映射關系實體類的構造函數
??????? /// 在構造函數中處理好映射關系
??????? /// </summary>
??????? public CustomerMapping()
??????? {
??????????? //指定持久化類對應的數據表
??????????? Table("TB_Customer");
??????? ????//自動增長的id
??????????? //Id(i => i.CustomerID);
??????????? //映射關系
??????????? Id<Guid>("CustomerID").GeneratedBy.GuidComb();
??????????? //指定主鍵后一定要加上主鍵字段的映射關系,不然返回的id為new Guid(),也就是一串0
??????????? Map(m => m.CustomerID).Nullable();
??????????? Map(m => m.CustomerAddress).Length(50).Nullable();
??????????? Map(m => m.CustomerName).Length(32).Nullable();
??????????? Map(m => m.Version);
??????????? //處理一對多關系的映射,一個客戶可以有多個訂單
??????????? //關聯的數據表進行懶加載,主鍵名為CustomerID,級聯關系所有操作,cascade:All|delete|saveorUpdate,級聯刪除時需加上Inverse()
??????????? //???? Inverse the ownership of this entity. Make the other side of the relationship
??????????? //???? responsible for saving.
??????????? HasMany<Order>(h => h.Orders).LazyLoad().AsSet().KeyColumn("CustomerID").Cascade.All().Inverse();
??????? }
?
首先是Tasks表的映射,因為Tasks表是多的一端,所以要添加對Projects表的外鍵引用關系,另外因為是一種外鍵引用不關系到父表的操作,所以這里可以使用Cascade.None()。
??? public TasksMappping()
??? {
??????? Table("Tasks");
??????? LazyLoad();
??????? Id(x => x.ID).Column("TaskID").GeneratedBy.Identity();
??????? References(x => x.Project).Nullable().Column("ProjectID").Cascade.None();//處理多對一關系,多個Tasks可以屬于一個Project
??????? Map(x => x.Name).Nullable();
??? }
在Projects表中,因為該表中的一個ID會對應多個Tasks所以在添加HasMany方法,來表明Projects和Tasks的多對一的關系,如下代碼它會涉及到任務的添加和更新操作,所以需要使用Cascade.SaveUpdate()。
??? public ProjectsMapping()
??? {
??????? Table("Projects");
??????? LazyLoad();
??????? Id(x => x.ID).Column("ProjectID").GeneratedBy.Identity();
??????? References(x => x.User).Column("UserID").Cascade.None(); //它的操作不會涉及到Projects的操作
??????? Map(x => x.Name).Nullable();
??????? HasMany(x => x.Task).KeyColumn("ProjectID").LazyLoad().Cascade.SaveUpdate();
//在save或者update Projects的時候會連帶著修改Tasks
???? }
3.1.2.補充說明
一對多的映射,比起一對一來說還相對的簡單點,默認是延遲加載,如果項目中,有些地方,需要立即加載,我們也可以使用 FetchMode.Eager 來加載;
?
4. 多對多映射 HasManyToMany <>()
4.1. 定義支持的關聯屬性
ParentKeyColumn??????? 定義與A表關聯的字段名
ChildKeyColumn???????? 定義與B表關聯的字段名
Table????????????????? 關系表
4.1.1. 映射范例
比如電子商務站的訂單和產品的關系
public ProductMap()
{
Table("Products");
Id(p => p.ProductID);
HasManyToMany<Order>(p => p.Orders)
.AsSet()
.LazyLoad()
.ParentKeyColumn("ProductID")
.ChildKeyColumn("OrderID")
.Table("OrderProduct");
Map(p => p.CreateTime);
Map(p => p.Name);
Map(p => p.Price);
}
public OrderMap()
{
Table("Orders");
Id(o => o.OrderID).GeneratedBy.Identity();
HasManyToMany<Product>(o => o.Products)
.AsSet()
.Not.LazyLoad()
.Cascade.All()
.ParentKeyColumn("OrderID")
.ChildKeyColumn("ProductID")
.Table("OrderProduct");
Map(o => o.Price);
Map(o => o.State).CustomType<OrderState>();
Map(o => o.Address);
Map(o => o.Coignee);
Map(o => o.CreateTime);
Map(o => o.Zip);
References<User>(o => o.User).Not.LazyLoad().Column("UserID"); //多個訂單屬于一個客戶
}
這里我們用了一個單獨的一個表OrderProduct來保存這個多對多關系
?
比如 一個Project會有有很多Product,同時一個Product也可能會在多個Project中
??? public ProjectsMapping()
??? {
??????? Table("Projects");
??????? LazyLoad();
??????? Id(x => x.ID).Column("ProjectID").GeneratedBy.Identity();
??????? References(x => x.User).Column("UserID").Cascade.None();
??????? Map(x => x.Name).Nullable();
??????? HasMany(x => x.Task).KeyColumn("ProjectID").LazyLoad().Cascade.SaveUpdate();
??????? HasManyToMany(x => x.Product).ParentKeyColumn("ProjectID").ChildKeyColumn("ProductID").Table("ProjectProduct");
??? }
??? public ProductMapping()
??? {
??????? Table("Product");
??????? Id(x => x.ID).Column("ProductID").GeneratedBy.Identity();
??????? Map(x => x.Name).Nullable();
??????? Map(x => x.Color).Nullable();
??????? HasManyToMany(x => x.Project).ParentKeyColumn("ProductID").ChildKeyColumn("ProjectID").Table("ProjectProduct");
??? }
4.1.2.補充說明
具體添加關聯的步驟如下:
(1)在映射的兩端同時添加HasManyToMany的關系這樣就形成了雙向的關聯關系
(2)指定映射的ParentKey和ChildKey,一般會將對象本身的ID指定為ParentKey,關聯對象的ID指定為ChildKey
(3)指定關聯關系的關系表,使用Table方法指定關聯表,如上示例的Table("ProjectProduct")。
?
?
以上只為突出一對一的雙向關聯映射,一對多的單向關聯和多對多的雙向關聯關系
轉載于:https://www.cnblogs.com/shy1766IT/p/4855176.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Fluent NHibernate关系映射的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 74. Search a 2D Matr
- 下一篇: 相似图片搜索的原理(转)