一、引言
NHibernate3.0增加了一種新的查詢API——QueryOver。QueryOver構(gòu)建在NHibernate原有的ICriteria API之上,支持Lambda表達(dá)式與擴(kuò)展方法,可編寫類型安全的查詢語(yǔ)句,這樣就克服了ICriteria API字符串硬編碼的弊端,可借助VS提供的智能提示方便代碼輸入,減少輸入錯(cuò)誤。同時(shí)可利用VS等重構(gòu)功能自動(dòng)更新因?qū)嶓w字段名更改而導(dǎo)致的查詢語(yǔ)句的變更,方便代碼重構(gòu)。本文主要介紹QueryOver的常見應(yīng)用,并結(jié)合一個(gè)可運(yùn)行的實(shí)例對(duì)各查詢場(chǎng)景進(jìn)行詳盡的闡述。
時(shí)間過(guò)得真快,離最近一次在博客園寫文章都快2年時(shí)間了。這些年工作是忙不到盡頭,很少有空閑的時(shí)間可以靜下心來(lái)寫博文,空閑時(shí)間有的話也看書、睡覺、跟新技術(shù)去了。不過(guò)有句名言說(shuō)得好:時(shí)間就像女人的乳溝擠擠總會(huì)有的,呵呵。最近剛結(jié)掉一個(gè)項(xiàng)目,利用難得的空隙機(jī)會(huì)研究了一下QueryOver,基本上實(shí)踐了項(xiàng)目中的大部分查詢場(chǎng)景,并計(jì)劃在后面的新項(xiàng)目中使用QueryOver。
只要熟悉ICriteria API與LINQ,玩玩QueryOver并不難,如果有個(gè)現(xiàn)成的詳實(shí)例子就會(huì)節(jié)省不少學(xué)習(xí)時(shí)間,但是網(wǎng)上關(guān)于QueryOver的可運(yùn)行的實(shí)例很少,再加上NHibernate在線幫助文檔對(duì)QueryOver的介紹一如既往的簡(jiǎn)潔,所以就做了個(gè)實(shí)例來(lái)介紹QueryOver,并在文章最后提供源代碼下載,以給剛接觸QueryOver的朋友分享經(jīng)驗(yàn)。
二、開發(fā)環(huán)境與工具
- NHibernate 3.2 Alpha2 :下載地址 http://sourceforge.net/projects/nhibernate/files/NHibernate/3.2.0Alpha2/
- Visual Studio 2010
- .NET Framework 4.0
- NUnit 2.5.10
- SQL Server 2005
三、實(shí)例場(chǎng)景
為便于理解與掌握,舉個(gè)故意簡(jiǎn)化的實(shí)例:客戶(Customer)與訂單(Order),一個(gè)客戶可以下多個(gè)訂單。實(shí)體類的代碼如下:
(1)實(shí)體基類: Entity
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace MyWorkShop.Model.Entities
7 {
8 public abstract class Entity<TId>
9 {
10 public virtual TId Id { get; protected set; }
11 }
12 }
(2)客戶類:Customer,映射的數(shù)據(jù)表為MyWorkShop_Customer
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace MyWorkShop.Model.Entities
7 {
8 public class Customer:Entity<int>
9 {
10 public virtual string Name { get; set; }
11 public virtual string Address { get; set; }
12 public virtual string Phone { get; set; }
13 }
14 }
(3)訂單類:Order,映射的數(shù)據(jù)表為MyWorkShop_Order
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace MyWorkShop.Model.Entities
7 {
8 //Guid做主鍵
9 public class Order : Entity<Guid>
10 {
11 //下單客戶
12 public virtual Customer Customer { get; set; }
13 //下單時(shí)間
14 public virtual DateTime OrderedDateTime { get; set; }
15 //訂單金額
16 public virtual Decimal? Amount { get; set; }
17 }
18 }
四、查詢場(chǎng)景
1. 篩選數(shù)據(jù)(Restriction)
(1)根據(jù)客戶姓名查找客戶,假設(shè)客戶姓名唯一
2 {
3 Customer entity = null;
4
5 using (var session = NHibernateSession)
6 using (var transaction = session.BeginTransaction())
7 {
8
9 entity = session.QueryOver<Customer>()
10 .Where(c => c.Name == customerName)
11 .SingleOrDefault();
12
13 transaction.Commit();
14 }
15 return entity;
16 }
輸出的SQL:
代碼說(shuō)明:
- 篩選條件調(diào)用Where方法,使用Lambda表達(dá)式“c => c.Name == customerName”,這樣就消除了ICriteria的字段名字符串硬編碼的問題;
- 返回單個(gè)值調(diào)用SingleOrDefault(),若查詢結(jié)果不唯一則拋出異常NHibernate.NonUniqueResultException。
(2)根據(jù)客戶地址查找多個(gè)客戶
2 {
3 IEnumerable<Customer> list = null;
4
5 using (var session = NHibernateSession)
6 using (var transaction = session.BeginTransaction())
7 {
8 list = session.QueryOver<Customer>()
9 .Where(c => c.Address == address)
10 .List();
11
12 transaction.Commit();
13 }
14
15 return list;
16 }
輸出的SQL:
代碼說(shuō)明:
- 查詢多條數(shù)據(jù)調(diào)用List()方法。
(3)根據(jù)客戶姓名模糊查找客戶
2 {
3 IEnumerable<Customer> list = null;
4
5 using (var session = NHibernateSession)
6 using (var transaction = session.BeginTransaction())
7 {
8 list = session.QueryOver<Customer>()
9 .WhereRestrictionOn(o => o.Name).IsLike(likeName, MatchMode.Anywhere)
10 .List();
11
12 transaction.Commit();
13 }
14
15 return list;
16 }
輸出的SQL:
代碼說(shuō)明:
- 對(duì)于某些SQL函數(shù)與操作符(比如like、between...and...),沒有直接對(duì)應(yīng)的Lambda表達(dá)式,需要先使用WhereRestrictionOn方法指定篩選條件的列,然后再調(diào)用相應(yīng)的方法指定篩選條件;
- IsLike方法指定字符串匹配查找。
(4)查找金額在指定范圍內(nèi)的訂單
3 {
4 IEnumerable<Order> list = null;
5
6 using (var session = NHibernateSession)
7 using (var transaction = session.BeginTransaction())
8 {
9 list = session.QueryOver<Order>()
10 .Where(o => o.Amount >= minAmount)
11 .And(o => o.Amount <= maxAmount)
12 .OrderBy(o => o.Amount).Desc
13 .List();
14
15 transaction.Commit();
16 }
17
18 return list;
19 }
輸出的SQL:
代碼說(shuō)明:
- 多個(gè)條件可使用Where...And...逐個(gè)指定,也可以在一個(gè)Where方法中指定,比如上面的條件可以寫成Where(o => o.Amount >= minAmount && o.Amount <= maxAmount);
- 排序使用OrderBy,升序降序使用Asc與Desc。
2.連接(Join)
(1)內(nèi)連接:根據(jù)客戶姓名查找訂單
2 {
3 IEnumerable<Order> list = null;
4
5 using (var session = NHibernateSession)
6 using (var transaction = session.BeginTransaction())
7 {
8 list = session.QueryOver<Order>()
9 .OrderBy(o=>o.Amount).Desc
10 .Inner.JoinQueryOver<Customer>(o => o.Customer)
11 .Where(c => c.Name == customerName)
12 .List();
13
14 transaction.Commit();
15 }
16
17 return list;
18 }
輸出的SQL:
代碼說(shuō)明:
- .Inner.JoinQueryOver指定內(nèi)連接,如果省略Inner僅寫JoinQueryOver默認(rèn)就是內(nèi)連接;
- .Left、.Right則分別為左外連接、右外連接
2 {
3 //定義用于內(nèi)連接的別名變量,該變量必須賦值為null
4 Customer customer = null;
5
6 IEnumerable<Order> list = null;
7
8 using (var session = NHibernateSession)
9 using (var transaction = session.BeginTransaction())
10 {
11 list = session.QueryOver<Order>()
12 .JoinAlias(o => o.Customer, () => customer) //指定別名customer
13 .Where(() => customer.Name == customerName)
14 .List();
15
16 transaction.Commit();
17 }
18
19 return list;
20 }
同上,略
- 可以通過(guò).Inner.JoinQueryOver來(lái)顯式進(jìn)行內(nèi)連接,也可以通過(guò).JoinAlias創(chuàng)建連接別名進(jìn)行連接;
- 連接別名變量在QueryOver使用之前定義,并且必須賦null值。
五、總結(jié)
與ICriteria API相比,個(gè)人認(rèn)為QueryOver并不見得會(huì)提高代碼的可讀性,但QueryOver解決了
本文通過(guò)一個(gè)簡(jiǎn)單的實(shí)例,介紹了QueryOver進(jìn)行條件篩選(Restriction)、連接(Join)等常見場(chǎng)景的應(yīng)用,在下一篇文章中將介紹投影(Projection)、把投影結(jié)果轉(zhuǎn)成DTO、分頁(yè)、子查詢(Subquery)等。
六、參考資料
[1] NHibernate關(guān)于QueryOver的在線幫助文檔
[2] NHibernate 3.0 Cookbook P130-137
七、實(shí)例源代碼下載
八、實(shí)例項(xiàng)目說(shuō)明
1. 項(xiàng)目組織結(jié)構(gòu)
- MyWorkShop.Model項(xiàng)目:創(chuàng)建實(shí)體類與DTO;
- MyWorkShop.Data項(xiàng)目:創(chuàng)建數(shù)據(jù)訪問接口;
- MyWorkShop.Data.NHibernate項(xiàng)目:定義數(shù)據(jù)訪問的NHibernate實(shí)現(xiàn),包含文中所列舉的各查詢方法;
- MyWorkShop.Data.NHibernate.Test項(xiàng)目:對(duì)MyWorkShop.Data.NHibernate項(xiàng)目中各數(shù)據(jù)訪問方法進(jìn)行測(cè)試。
2. 測(cè)試數(shù)據(jù)庫(kù)
3. NHibernate配置文件
hibernate.cfg.xml位于MyWorkShop.Data.NHibernate.Test項(xiàng)目中的,可根據(jù)自己的運(yùn)行環(huán)境進(jìn)行相應(yīng)的配置。
浙公網(wǎng)安備 33010602011771號(hào)