Nhibernate+SQLite 入门实例指南二 类的继承、多态关系
?昨天忘記向源代碼下載了,現(xiàn)在補(bǔ)上第一章的代碼:http://files.cnblogs.com/9527/QuickStart1.rar
實(shí)例二、類的繼承、多態(tài)關(guān)系
在我們實(shí)際設(shè)計(jì)過程中,經(jīng)常碰到類的繼承關(guān)系,比如一個(gè)電子產(chǎn)品商店,同時(shí)銷售手機(jī)和MP3,所以在設(shè)計(jì)系統(tǒng)的時(shí)候我們把手機(jī)和MP3的共性如品牌、名稱等抽象為一個(gè)類,而把它們的特性比如MP3有內(nèi)存容量,手機(jī)有號(hào)碼等,我們以不同的子類來體現(xiàn)。如下圖:
在實(shí)際數(shù)據(jù)庫的時(shí)候,最簡(jiǎn)單的就是每個(gè)子類擁有一個(gè)獨(dú)立的表分別對(duì)應(yīng)Mp3player和MobilePhone
由于MobilePhone和Mp3Player都是繼承electronic,所以他們都Id,Name,Brand等屬性,了我們分別為兩個(gè)表編寫映射文件:
MobilePhone.hbm.xml
<?xml?version="1.0"?encoding="utf-8"??>?<hibernate-mapping?xmlns="urn:nhibernate-mapping-2.0">?
<class?name="QuickStart2.Data.MobilePhone,?QuickStart2.Data"?table="t_mobilephone"?>?
<id?name="Id"?column="id"?type="Int32">?
<generator?class="identity"?/>?
</id>?
<property?name="Name"?type="String(100)"?column="name"?/>?
<property?name="Brand"?type="String(20)"?column="brand"?/>?
<property?name="Phonenumber"?type="String(13)"?column="phonenumber"?/>?
</class>?
</hibernate-mapping>?
Mp3Player.hbm.xml?
<?xml?version="1.0"?encoding="utf-8"??>?
<hibernate-mapping?xmlns="urn:nhibernate-mapping-2.0">?
<class?name="QuickStart2.Data.Mp3Player,?QuickStart2.Data"?table="t_mp3player"?>?
<id?name="Id"?column="id"?type="Int32">?
<generator?class="identity"?/>?
</id>?
<property?name="Name"?type="String(100)"?column="name"?/>?
<property?name="Brand"?type="String(20)"?column="brand"?/>?
<property?name="Mensize"?type="Int32"?column="mensize"?/>?
</class>?
</hibernate-mapping>?
可以看到這兩個(gè)映射和普通的映射文件沒有什么不同。我們編寫了一個(gè)段測(cè)試代碼:
?
ISession?session=null;?ArrayList?list=null;?
try{?
session=SessionFactory.OpenSession();?
list=(ArrayList)session.CreateCriteria(typeof(electronic)).List();?
}?
catch(Exception?e)?{?
System.Console.WriteLine(e.Message);?
System.Console.ReadLine();?
}?
finally{?
session.Close();?
}?
foreach(electronic?el?in?list)?{?
System.Console.WriteLine("名稱:o"+el.Name);?
}?
運(yùn)行結(jié)果如下圖所示:
我們看到NHibernate更據(jù)electronic自動(dòng)找到它的兩個(gè)子類Mp3Player和MobilePhone,生存兩鐵絲SQL語句分別查詢兩個(gè)表,下面輸出的是,查詢出來的結(jié)果。
為什么會(huì)這樣子,在NHibernate中是用多態(tài)(polymorphism)來進(jìn)行描述兩個(gè)繼承于同一父類的子類情況,在映射文件的Class節(jié)點(diǎn)中也有一個(gè)polymorphism的屬性用來設(shè)定是隱式還是顯式的使用查詢多態(tài),為什么我們的映射類中沒有出現(xiàn)這個(gè)屬性的設(shè)定呢,polymorphism?=?implicit是默認(rèn)的。所以我們?nèi)绻M(jìn)行的是隱式的查詢就可以不用進(jìn)行設(shè)定。以下是摘自博客園renrenqq?翻譯的NHibernate中文文檔中對(duì)polymorphism的解釋:
Implicit (隱式)的多態(tài)是指,如果查詢中給出的是任何超類、該類實(shí)現(xiàn)的接口或者該類的名字,都會(huì)返回這個(gè)類的實(shí)例;如果查詢中給出的是子類的名字,則會(huì)返回子類的實(shí)例。Explicit?(顯式)的多態(tài)是指,只有在查詢中給出的明確是該類的名字時(shí)才會(huì)返回這個(gè)類的實(shí)例;同時(shí)只有當(dāng)在這個(gè)?<class>?的定義中作為?<subclass>?或者?<joined-subclass>?出現(xiàn)的子類,才會(huì)可能返回。?大多數(shù)情況下,默認(rèn)的polymorphism="implicit"都是合適的。?顯式的多態(tài)在有兩個(gè)不同的類映射到同一個(gè)表的時(shí)候很有用。(允許一個(gè)“輕型”的類,只包含部分表字段)。
通過這樣的方式我們雖然也實(shí)現(xiàn)了我們想要的結(jié)果,但是對(duì)經(jīng)驗(yàn)豐富的設(shè)計(jì)人員來說,這樣的設(shè)計(jì)是并不合理的,一旦父類分生了變動(dòng),數(shù)據(jù)庫里兩個(gè)表都必須修改,而且從性能角度上來說也并不怎么理想。
所以,更據(jù)經(jīng)驗(yàn)我們一般會(huì)吧兩個(gè)表相同的字段合并成一個(gè)表,另建兩個(gè)新表,只包含Mp3Player和MobilePhone的特有的屬性。經(jīng)過修改我們的數(shù)據(jù)庫結(jié)構(gòu)如下圖如示:
可以看到主表于子表之間通過外鍵向連。
數(shù)據(jù)庫修改以后,我們要重新設(shè)計(jì)映射文件,讓它正確的映射到關(guān)系型數(shù)據(jù)庫。
Electronic.hbm.xml
?
<?xml?version="1.0"?encoding="utf-8"??>?<hibernate-mapping?xmlns="urn:nhibernate-mapping-2.0">?
<class?name="QuickStart2.Data.electronic,?QuickStart2.Data"?table="t_electronic">?
<id?name="Id"?column="id"?type="Int32">?
<generator?class="identity"?/>?
</id>?
<property?name="Name"?type="String(100)"?column="name"?/>?
<property?name="Brand"?type="String(20)"?column="brand"?/>?
<joined-subclass?name="QuickStart2.Data.MobilePhone,QuickStart2.Data"?table="t_mobile">?
<key?column="id"?/>?
<property?name="Phonenumber"?type="String(13)"?column="phonenumber"?/>?
</joined-subclass>?
<joined-subclass?name="QuickStart2.Data.Mp3Player,QuickStart2.Data"?table="t_mp3">?
<key?column="id"?/>?
<property?name="Mensize"?type="Int32"?column="mensize"?/>?
</joined-subclass>?
</class>?
</hibernate-mapping>?
通過映射文件我們可以看到,我們?yōu)橹黝惤⒘擞成?#xff0c;兩個(gè)子類在<joined-subclass>節(jié)點(diǎn)下進(jìn)行了配置。
我們現(xiàn)編寫一段數(shù)據(jù)插入代碼來看看,NHibernate是怎么進(jìn)行數(shù)據(jù)插入的
?
public?static?void?SaveMobilephone()?{?
ISession?session=null;?
ITransaction?t=null;?
try?
{?
session=SessionFactory.OpenSession();?
t=session.BeginTransaction();?
MobilePhone?mp=new?MobilePhone();?
mp.Name="575";?
mp.Brand="Dopod";?
mp.Phonenumber="13151921698";?
Mp3Player?mp3=new?Mp3Player();?
mp3.Name="SB200";?
mp3.Brand="小霸王";?
mp3.Mensize=513;?
session.Save(mp3);?
session.Save(mp);?
session.Flush();?
t.Commit();?
}?
catch(HibernateException?e)?
{?
System.Console.Write(e.Message.ToString());?
if(t!=null)?
{?
try?
{?
t.Rollback();?
}?
catch(HibernateException?e1)?
{?
System.Console.Write(e1.Message.ToString());?
}?
}?
}?
finally?
{?
session.Close();?
}?
System.Console.ReadLine();?
}?
編譯運(yùn)行如上代碼,在返回中我們看到如下結(jié)果
根據(jù)圖片,我們可以看到,和前一次不同,NHibernate分四次,分別對(duì)主表和子表進(jìn)行插入。
在運(yùn)行我們?cè)谏弦还?jié)中的查詢代碼,其結(jié)果是
我們可以看到,NHibernate用了一條組合查詢,一次性就把三個(gè)表所有數(shù)據(jù)查詢了出來。
從上面的例子我們可以看到,這樣的設(shè)計(jì),雖然從設(shè)計(jì)的邏輯上來說更加清晰明了,符合我們的設(shè)計(jì)習(xí)慣,但是多表操作帶來的性能的消耗來是相當(dāng)?shù)目捎^,因此,如果我們的系統(tǒng)對(duì)性能上有相當(dāng)?shù)囊?#xff0c;我們就要考慮第三種方式。
我們把數(shù)據(jù)設(shè)計(jì)成不僅包含用父類的共同屬性,也同是用不同的字段來表示兩種產(chǎn)品的不同特性,這樣如何來區(qū)別兩個(gè)類呢,我們?cè)跀?shù)據(jù)庫中加入一個(gè)”category”的字段來區(qū)別兩種產(chǎn)品。
代碼方面我們需要修改的也只有數(shù)據(jù)庫的映射文件,把新映射文件修改成如下:
?
<?xml?version="1.0"?encoding="utf-8"??>?<hibernate-mapping?xmlns="urn:nhibernate-mapping-2.0">?
<class?name="QuickStart2.Data.electronic,?QuickStart2.Data"?table="t_elec">?
<id?name="Id"?column="id"?type="Int32">?
<generator?class="identity"?/>?
</id>?
<discriminator?column="category"?type="String(10)"?/>?
<property?name="Name"?type="String(100)"?column="name"?/>?
<property?name="Brand"?type="String(20)"?column="brand"?/>?
<subclass?name="QuickStart2.Data.MobilePhone,QuickStart2.Data"?discriminator-value="1"?>?
<property?name="Phonenumber"?type="String(13)"?column="phonenumber"?/>?
</subclass>?
<subclass?name="QuickStart2.Data.Mp3Player,QuickStart2.Data"?discriminator-value="2"?>?
<property?name="Mensize"?type="Int32"?column="mensize"?/>?
</subclass>?
</class>?
</hibernate-mapping>?
于前一節(jié)中的映射文件不一樣的是,我們?cè)谟成湮募屑尤肓艘粋€(gè)識(shí)別器(discriminator),用來指定數(shù)據(jù)庫中哪個(gè)字段是用來做為標(biāo)識(shí)的。
在定義的子類的時(shí)候,也不能用joined-subclass 而要改成subclass joined-subclass不能支持discriminator-value屬性,discriminator-value就是用來區(qū)分獨(dú)立的子類當(dāng)我們完成數(shù)據(jù)庫的插入操作后(具體代碼可以用上一節(jié)所提供的代碼,由于代碼完全相同,所以不再次提供了)我們可以看到,NHibernate只用了兩次插入動(dòng)作就完成了對(duì)數(shù)據(jù)的插入。
我們?cè)龠\(yùn)行查詢的代碼也可以用一條簡(jiǎn)單的SQL語句來完成操作,大家也可以試著用
list=(ArrayList)session.CreateQuery("from Mp3player").List();
或
list=(ArrayList)session.CreateQuery("from MobilePhone").List();
進(jìn)行查詢,在NHibernate生的結(jié)果如下圖:
可以看到,NHibernate在讀取數(shù)據(jù)的時(shí)候會(huì)自動(dòng)更據(jù)標(biāo)識(shí)讀取Mp3Player或MobilePhone的內(nèi)容。
但是這種方法也有它的硬傷,如果子類的特性太多,哪數(shù)據(jù)庫的可讀性就大打折扣了,甚至可以讓人抓狂,對(duì)于系統(tǒng)的后期維護(hù)升級(jí)非常的不利。
因此、三種方法各有各的優(yōu)點(diǎn)也各有缺點(diǎn),我們要在設(shè)計(jì)的過程中更據(jù)實(shí)際情況進(jìn)于權(quán)衡,然后才確定采用何種方式。
第二章代碼下載:http://files.cnblogs.com/9527/QuickStart2.rar
? ? 本文轉(zhuǎn)自無心之柳.NET博客園博客,原文鏈接:http://www.cnblogs.com/9527/archive/2006/09/24/513505.html,如需轉(zhuǎn)載請(qǐng)自行聯(lián)系原作者
總結(jié)
以上是生活随笔為你收集整理的Nhibernate+SQLite 入门实例指南二 类的继承、多态关系的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 谷歌 Pixel 6 魔法橡皮擦功能下放
- 下一篇: 在LINQ to SQL中使用Trans