LINQ to SQL與NHibernate橫向?qū)Ρ?/h1>
1 引言
研發(fā)與數(shù)據(jù)庫(kù)打交道的系統(tǒng)的時(shí)候,最過(guò)于繁瑣的莫過(guò)于沒(méi)有編程快感的使用ADO.NET對(duì)后臺(tái)數(shù)據(jù)庫(kù)進(jìn)行操作,因?yàn)樗械臄?shù)據(jù)庫(kù)連接、讀取、操作千篇一律,編程成為了體力活。
雖然我們可以設(shè)計(jì)自己的類作為數(shù)據(jù)庫(kù)訪問(wèn)的持久層,但是每一個(gè)類都必須有不相同的SQL語(yǔ)句,這樣對(duì)于設(shè)計(jì)統(tǒng)一的數(shù)據(jù)庫(kù)讀寫類造成了很大的困難。
開(kāi)發(fā)人員在這種情況下必須包辦窗體設(shè)計(jì)、方法設(shè)計(jì)、數(shù)據(jù)庫(kù)讀寫設(shè)計(jì)的過(guò)程,這樣加大了開(kāi)發(fā)人員的負(fù)擔(dān)也使得項(xiàng)目的維護(hù)和后期開(kāi)發(fā)變得難以進(jìn)行。
2 .NET下的ORM解決方案
2.1 LINQ
2.1.1 LINQ簡(jiǎn)介
作為微軟開(kāi)發(fā)的查詢方案,LINQ 提供了一條更常規(guī)的途徑即給 .Net Framework 添加一些可以應(yīng)用于所有信息源( all sources of information )的具有多種用途( general-purpose )的語(yǔ)法查詢特性( query facilities ),這是比向開(kāi)發(fā)語(yǔ)言和運(yùn)行時(shí)( runtime )添加一些關(guān)系數(shù)據(jù)( relational )特性或者類似 XML 特性( XML-specific )更好的方式。這些語(yǔ)法特性就叫做 .NET Language Integrated Query (LINQ) 。
如果覺(jué)得上面的解釋有點(diǎn)抽象,那么可以這樣理解,LINQ其實(shí)就是提供了一套查詢功能,可以實(shí)現(xiàn)任何數(shù)據(jù)源的查詢,此處數(shù)據(jù)源不單指數(shù)據(jù)庫(kù)或者XML文件,而是任何集合或者實(shí)體,比如我們接觸各種編程語(yǔ)言都需要用到的數(shù)組,現(xiàn)在不用遍歷數(shù)組元素來(lái)尋找需要的項(xiàng),LINQ可以實(shí)現(xiàn)這方面的查詢。
LINQ查詢數(shù)組:
圖2.1 LINQ查詢數(shù)組
上面是最簡(jiǎn)單的LINQ實(shí)現(xiàn)對(duì)數(shù)組的查詢,泛型類型var在LINQ查詢中提供了強(qiáng)大的委托類型支持,不管查詢集合中項(xiàng)的類型(無(wú)論是int,char還是string或者類),我們只用一個(gè)var就可以保存LINQ查詢到的結(jié)果。程序結(jié)果如下:
圖2.2 LINQ查詢數(shù)組程序結(jié)果
是不是很方便,LINQ的應(yīng)用遠(yuǎn)遠(yuǎn)不這些,通過(guò)不同的映射方案,我們可以實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)(LINQ To SQL),對(duì)XML文件(LINQ To XML)的訪問(wèn)。
2.1.2 LINQ簡(jiǎn)介
表2.1 LINQ的操作符
操作符 |
說(shuō)明 |
聚合 |
|
Aggregate |
對(duì)序列執(zhí)行一個(gè)自定義方法 |
Average |
計(jì)算數(shù)值序列的平均值 |
Count |
返回序列中的項(xiàng)目數(shù)(整數(shù)) |
LongCount |
返回序列中的項(xiàng)目數(shù)(長(zhǎng)型) |
Min |
查找數(shù)字序列中的最小數(shù) |
Max |
查找數(shù)字序列中的***數(shù) |
Sum |
匯總序列中的數(shù)字 |
連接 |
|
Concat |
將兩個(gè)序列連成一個(gè)序列 |
轉(zhuǎn)換 |
|
Cast |
將序列中的元素轉(zhuǎn)換成指定類型 |
OfType |
篩選序列中指定類型的元素 |
ToArray |
從序列返回一個(gè)數(shù)組 |
ToDictionary |
從序列返回一個(gè)字典 |
ToList |
從序列返回一個(gè)列表 |
ToLookup |
從序列返回一個(gè)查詢 |
ToSequence |
返回一個(gè) IEnumerable 序列 |
元素 |
|
DefaultIfEmpty |
為空序列創(chuàng)建默認(rèn)元素 |
ElementAt |
返回序列中指定索引的元素 |
ElementAtOrDefault |
返回序列中指定索引的元素,或者如果索引超出范圍,則返回默認(rèn)值 |
First |
返回序列中的***個(gè)元素 |
FirstOrDefault |
返回序列中的***個(gè)元素,或者如果未找到元素,則返回默認(rèn)值 |
Last |
返回序列中的***一個(gè)元素 |
LastOrDefault |
返回序列中的***一個(gè)元素,或者如果未找到元素,則返回默認(rèn)值 |
Single |
返回序列中的單個(gè)元素 |
SingleOrDefault |
返回序列中的單個(gè)元素,或者如果未找到元素,則返回默認(rèn)值 |
相等 |
|
SequenceEqual |
比較兩個(gè)序列看其是否相等 |
生成 |
|
Empty |
生成一個(gè)空序列 |
Range |
生成一個(gè)指定范圍的序列 |
Repeat |
通過(guò)將某個(gè)項(xiàng)目重復(fù)指定次數(shù)來(lái)生成一個(gè)序列 |
分組 |
|
GroupBy |
按指定分組方法對(duì)序列中的項(xiàng)目進(jìn)行分組 |
聯(lián)接 |
|
GroupJoin |
通過(guò)歸組將兩個(gè)序列聯(lián)接在一起 |
Join |
將兩個(gè)序列從內(nèi)部聯(lián)接起來(lái) |
排序 |
|
OrderBy |
以升序按值排列序列 |
OrderByDescending |
以降序按值排列序列 |
ThenBy |
升序排列已排序的序列 |
ThenByDescending |
降序排列已排序的序列 |
Reverse |
顛倒序列中項(xiàng)目的順序 |
分區(qū) |
|
Skip |
返回跳過(guò)指定數(shù)目項(xiàng)目的序列 |
SkipWhile |
返回跳過(guò)不滿足表達(dá)式項(xiàng)目的序列 |
Take |
返回具有指定數(shù)目項(xiàng)目的序列 |
TakeWhile |
返回具有滿足表達(dá)式項(xiàng)目的序列 |
投影 |
|
Select |
創(chuàng)建部分序列的投影 |
SelectMany |
創(chuàng)建部分序列的一對(duì)多投影 |
限定符 |
|
All |
確定序列中的所有項(xiàng)目是否滿足某個(gè)條件 |
Any |
確定序列中是否有任何項(xiàng)目滿足條件 |
Contains |
確定序列是否包含指定項(xiàng)目 |
限制 |
|
Where |
篩選序列中的項(xiàng)目 |
設(shè)置 |
|
Distinct |
返回?zé)o重復(fù)項(xiàng)目的序列 |
Except |
返回代表兩個(gè)序列差集的序列 |
Intersect |
返回代表兩個(gè)序列交集的序列 |
Union |
返回代表兩個(gè)序列交集的序列 |
Lambda 表達(dá)式
許多標(biāo)準(zhǔn)查詢操作符在對(duì)序列執(zhí)行運(yùn)算時(shí)都使用 Func 委托來(lái)處理單個(gè)元素。Lambda 表達(dá)式可與標(biāo)準(zhǔn)查詢操作符結(jié)合使用以代表委托。lambda 表達(dá)式是創(chuàng)建委托實(shí)現(xiàn)的簡(jiǎn)略表達(dá)形式,并可用于匿名委托適用的所有場(chǎng)合。C# 和 Visual Basic® .NET 均支持 Lambda 表達(dá)式。但是,必須注意:由于 Visual Basic .NET 尚不支持匿名方法,Lambda 表達(dá)式可能僅包含一個(gè)語(yǔ)句。
上例中的的程序等同于下面
圖2.3 Lambda表達(dá)式的使用
2.2 NHibernate
說(shuō)到NHibernate,就不得不提Hibernate,原因很簡(jiǎn)單,Hibernate顧名思義就是Hibernate的.NET版本。
Hibernate是一個(gè)開(kāi)放源代碼的對(duì)象關(guān)系映射框架,它對(duì)JDBC進(jìn)行了非常輕量級(jí)的對(duì)象封裝,使得Java程序員可以隨心所欲的使用對(duì)象編程思維來(lái)操縱數(shù)據(jù)庫(kù)。 Hibernate可以應(yīng)用在任何使用JDBC的場(chǎng)合,既可以在Java的客戶端程序使用,也可以在Servlet/JSP的Web應(yīng)用中使用,***革命意義的是,Hibernate可以在應(yīng)用EJB的J2EE架構(gòu)中取代CMP,完成數(shù)據(jù)持久化的重任。
NHibernate作為Hibernate的.NET應(yīng)用于Hibernate的實(shí)現(xiàn)完全相同,學(xué)習(xí)NHibernate完全可以直接學(xué)習(xí)Hibernate的資料。
事實(shí)上,雖然在Java數(shù)據(jù)庫(kù)映射領(lǐng)域Hibernate是使用最為廣泛的方案,但是在.NET中由于LINQ等映射方案(包括微軟下一代重量級(jí)的Entity Framework)的使用,NHibernate冷了許多。
NHibernate需要配置數(shù)據(jù)庫(kù)配置文件和類/表映射配置文件,所以使用NHibernate需要懂得XML文件的基礎(chǔ)知識(shí),并且需要掌握比較復(fù)雜的XML文件配置節(jié)和相應(yīng)的配置命令。
2.2.1 數(shù)據(jù)庫(kù)配置文件
NHibernate官方提供了配置文件的模板和實(shí)例可供我們參考。
圖2.4 NHibernate官方數(shù)據(jù)庫(kù)配置文件模板(對(duì)應(yīng)了不同的數(shù)據(jù)庫(kù))
上圖為數(shù)據(jù)庫(kù)配置文件。通常以“cfg.xml”作為后綴,一個(gè)示例的文件內(nèi)容如下
圖2.5 數(shù)據(jù)庫(kù)配置文件示例
下面是一些在運(yùn)行時(shí)可以改變NHibernate行為的其他配置。所有這些都是可選的,也有合理的默認(rèn)值。
表2.2 NHibernate 配置屬性
屬性名 |
用途 |
hibernate.dialect |
NHibernate方言(Dialect)的類名 - 可以讓NHibernate使用某些特定的數(shù)據(jù)庫(kù)平臺(tái)的特性 例如: full.classname.of.Dialect(如果方言創(chuàng)建在NHibernate中), 或者full.classname.of.Dialect, assembly (如果使用一個(gè)自定義的方言的實(shí)現(xiàn),它不屬于NHibernate)。 |
hibernate.default_schema |
在生成的SQL中,scheml/tablespace的全限定名. 例如: SCHEMA_NAME |
hibernate.prepare_sql |
是否準(zhǔn)備sql語(yǔ)句 例如: true | false |
hibernate.session_factory_name |
SessionFactory被創(chuàng)建后將自動(dòng)綁定這個(gè)名稱. 例如: some.name |
hibernate.use_outer_join |
允許使用外連接抓取。 例如:true | false |
hibernate.cache.provider_class |
指定一個(gè)自定義的CacheProvider緩存提供者的類名 例如: full.classname.of.CacheProvider(如果ICacheProvider創(chuàng)建在NHibernate中), 或full.classname.of.CacheProvider, assembly(如果使用一個(gè)自定義的ICacheProvider,它不屬于NHibernate)。 |
hibernate.query.substitutions |
把NHibernate查詢中的一些短語(yǔ)替換為SQL短語(yǔ)(比如說(shuō)短語(yǔ)可能是函數(shù)或者字符)。 例如: hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC |
2.2.2 實(shí)體映射配置文件
NHibernate官方開(kāi)源包中提供了實(shí)體映射配置文件的實(shí)例可供我們參考。
圖2.6 NHibernate開(kāi)源包中提供的實(shí)體映射配置文件
與數(shù)據(jù)庫(kù)配置文件一樣實(shí)體映射配置文件也是XML文件(XML果然是很強(qiáng)大啊,微軟下一代應(yīng)用程序開(kāi)發(fā)技術(shù)WPF就是使用XML文件將C/S和B/S長(zhǎng)期分居的二人統(tǒng)一到一個(gè)屋檐下),所不同的是實(shí)體映射配置文件后綴是“hbm.xml”。
圖2.7 實(shí)體映射配置文件
實(shí)體映射配置文件所要配置的信息一般為
Ø Schema
所有的XML映射都需要使用nhibernate-mapping-2.0 schema。目前的schema可以在NHibernate的資源路徑或者是NHibernate.dll的嵌入資源(Embedded Resource)中找到。NHibernate總是會(huì)優(yōu)先使用嵌入在資源中的schema文件。
Ø hibernate-mapping
(1) |
schema (可選): 數(shù)據(jù)庫(kù)schema名稱. |
(2) |
default-cascade (可選 - 默認(rèn)為 none): 默認(rèn)的級(jí)聯(lián)風(fēng)格. |
(3) |
auto-import (可選 - 默認(rèn)為 true): 指定是否我們可以在查詢語(yǔ)言中使用非全限定的類名(僅限于本映射文件中的類)。 |
(4) |
default-access (可選 - 默認(rèn)為 property): NHibernate訪問(wèn)屬性值時(shí)的策略。 |
(5) |
assembly (可選): 指定一個(gè)程序集,如果在映射文檔中沒(méi)有指定程序集,就使用這個(gè)程序集。 |
(6) |
namespace (可選): 指定一個(gè)命名空間前綴,如果在映射文檔中沒(méi)有指定全限定名,就使用這個(gè)命名空間名。 |
Ø class
(1) |
name: 持久化類(或者接口)的全限定名。 |
(2) |
table: 對(duì)應(yīng)的數(shù)據(jù)庫(kù)表名。 |
(3) |
discriminator-value (可選 - 默認(rèn)和類名一樣): 一個(gè)用于區(qū)分不同的子類的值,在多態(tài)行為時(shí)使用。 |
(4) |
mutable (可選, 默認(rèn)為 true): 表明該類的實(shí)例可變(不可變)。 |
(5) |
schema (可選): 覆蓋在根 |
(6) |
proxy (可選): 指定一個(gè)接口,在延遲裝載時(shí)作為代理使用。你可以在這里使用該類自己的名字。 |
(7) |
dynamic-update (可選, 默認(rèn)為 false): 指定用于UPDATE 的SQL將會(huì)在運(yùn)行時(shí)動(dòng)態(tài)生成,并且只更新那些改變過(guò)的字段。 |
(8) |
dynamic-insert (可選, 默認(rèn)為 false): 指定用于INSERT的 SQL 將會(huì)在運(yùn)行時(shí)動(dòng)態(tài)生成,并且只包含那些非空值字段。 |
(9) |
polymorphism (可選, 默認(rèn)為 implicit(隱式)): 界定是隱式還是顯式的使用查詢多態(tài)。 |
(10) |
where (可選) 指定一個(gè)附加的SQL WHERE 條件,在抓取這個(gè)類的對(duì)象時(shí)會(huì)一直增加這個(gè)條件。 |
(11) |
persister (可選): 指定一個(gè)定制的 IClassPersister. |
(12) |
lazy(可選):假若設(shè)置 lazy="true",就是設(shè)置這個(gè)類自己的名字作為proxy接口的一種等價(jià)快捷形式。 |
Ø id
(1) |
name (可選): 標(biāo)識(shí)屬性的名字。 |
(2) |
type (可選): 標(biāo)識(shí)NHibernate類型的名字。 |
(3) |
column (可選 - 默認(rèn)為屬性名): 主鍵字段的名字。 |
(4) |
unsaved-value (可選 - 默認(rèn)為 null): 一個(gè)特定的標(biāo)識(shí)屬性值,用來(lái)標(biāo)志該實(shí)例是剛剛創(chuàng)建的,尚未保存。這可以把這種實(shí)例和從以前的session中裝載過(guò)(可能又做過(guò)修改--譯者注)但未再次持久化的實(shí)例區(qū)分開(kāi)來(lái)。 |
(5) |
access (可選 - 默認(rèn)為 property): NHibernate用來(lái)訪問(wèn)屬性值的策略。 |
除此之外我們可以通過(guò)其他途徑深入了解配置方面的知識(shí),一個(gè)NHibernate項(xiàng)目,配置文件的錯(cuò)誤往往導(dǎo)致錯(cuò)誤的結(jié)果甚至使得程序無(wú)法運(yùn)行。
3 小結(jié)
本文初步介紹了LINQ to SQL和NHibernate,其中介紹NHibernate使用了較多的篇幅,因?yàn)橄鄬?duì)LINQ to SQL而言NHibernate的使用入門門檻較高,配置較為復(fù)雜。關(guān)于LINQ to SQL、NHibernate優(yōu)缺點(diǎn)將在后面文章中討論,不過(guò)從此處其實(shí)已經(jīng)得出一點(diǎn),那就是LINQ to SQL比NHibernate更加容易上手,節(jié)省了人員培訓(xùn)的開(kāi)銷。
【編輯推薦】