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