一、引言
NHibernate3.0增加了一种新的查询API——QueryOver。QueryOver构建在NHibernate原有的 ICriteria API之上,支持Lambda表达式与扩展方法,可编写类型安全的查询语句,这样就克服了ICriteria API字符串硬编码的弊端。在上一篇文章中《NHibernate 3.x新功能实践(一) QueryOver(上)》通过一个简单的实例,介绍了QueryOver进行条件筛选(Restriction)、连接(Join)等应用,在这篇文章中将介绍投影(Projection)、把投影结果转成DTO、分页、子查询(Subquery)等常见应用场景。 在文章《NHibernate 3.x新功能实践(一) QueryOver(上)》最后提供实例源代码下载。
二、开发环境与工具
三、实例场景
参见《 NHibernate 3.x新功能实践(一) QueryOver(上)》四、查询场景
1. 投影且把投影结果转成DTO (Projection)
订单DTO类:OrderDTO
1
using
System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace MyWorkShop.Model.DTOs
7 {
8 public class OrderDTO
9 {
10 public Guid Id { get ; set ; }
11 public string CustomerName { get ; set ; }
12 public DateTime OrderedDateTime { get ; set ; }
13 public Decimal ? Amount { get ; set ; }
14 }
15 }
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace MyWorkShop.Model.DTOs
7 {
8 public class OrderDTO
9 {
10 public Guid Id { get ; set ; }
11 public string CustomerName { get ; set ; }
12 public DateTime OrderedDateTime { get ; set ; }
13 public Decimal ? Amount { get ; set ; }
14 }
15 }
(1)根据订单号查找订单,并用LINQ TO Object转成OrderDTO
1
public
OrderDTO GetOrderDTOById(Guid id)
2 {
3 OrderDTO dto = null ;
4
5 Customer customer = null ;
6
7 using (var session = NHibernateSession)
8 using (var transaction = session.BeginTransaction())
9 {
10 dto = session.QueryOver < Order > ()
11 .JoinAlias(o => o.Customer, () => customer)
12 .Where(o => o.Id == id)
13 . Select(o => o.Id, o => customer.Name, o => o.OrderedDateTime,o => o.Amount)
14 .List < object [] > ()
15 .Select (props => new OrderDTO
16 {
17 Id = ( Guid ) props[ 0 ],
18 CustomerName = ( string ) props[ 1 ],
19 OrderedDateTime = ( DateTime ) props[ 2 ],
20 Amount = ( decimal ) props[ 3 ]
21 }).SingleOrDefault ( );
22
23 transaction.Commit();
24 }
25
26 return dto;
27 }
2 {
3 OrderDTO dto = null ;
4
5 Customer customer = null ;
6
7 using (var session = NHibernateSession)
8 using (var transaction = session.BeginTransaction())
9 {
10 dto = session.QueryOver < Order > ()
11 .JoinAlias(o => o.Customer, () => customer)
12 .Where(o => o.Id == id)
13 . Select(o => o.Id, o => customer.Name, o => o.OrderedDateTime,o => o.Amount)
14 .List < object [] > ()
15 .Select (props => new OrderDTO
16 {
17 Id = ( Guid ) props[ 0 ],
18 CustomerName = ( string ) props[ 1 ],
19 OrderedDateTime = ( DateTime ) props[ 2 ],
20 Amount = ( decimal ) props[ 3 ]
21 }).SingleOrDefault ( );
22
23 transaction.Commit();
24 }
25
26 return dto;
27 }
输出的SQL:
SELECT
this_.Id
as
y0_, customer1_.Name
as
y1_, this_.OrderedDateTime
as
y2_, this_.Amount
as
y3_
FROM
MyWorkShop_Order this_
inner
join
MyWorkShop_Customer customer1_
on
this_.CustomerId
=
customer1_.Id
WHERE
this_.Id
=
@p0
;
@p0
=
b0a7f211
-
0404
-
4df5
-
93be
-
9ee501216c5c
代码说明:
- 由于OrderDTO包含CustomerName字段,而该字段的值取自Customer实体类,所以需对Order与Customer进行内连接,关于内连接的操作请参见上一篇文章《NHibernate 3.x新功能实践(一) QueryOver(上)》;
- 代码中的第一个Select进行投影(Projection)操作,取出所要的4个字段,分别为o.Id、customer.Name、o.OrderedDateTime、o.Amount;
- .List<object[]>()把投影得到的4个字段放到一个object[]数组中;
- 代码中的第二个Select使用LINQ TO Object(此时与NHibernate无关),新建一个OrderDTO对象,并把object[]数组的4个字段依次赋给OrderDTO对象,字段赋值之前需进行强制类型转换,把object类型转成相应的类型;
- 由于需对每个字段进行强制类型转换,所以代码不太干净且容易出错,而且当字段类型变化时需手工修改代码,不利于代码重构,所以不推荐使用此方案,较好的方案是下面介绍的使用NHibernate内置方法把投影结果转成DTO。
1
public
OrderDTO GetOrderDTOById(Guid id)
2 {
3 OrderDTO dto = null ;
4
5 // 定义用于内连接的别名变量,该变量必须赋值为null
6 Customer customer = null ;
7
8 using (var session = NHibernateSession)
9 using (var transaction = session.BeginTransaction())
10 {
11 dto = session.QueryOver < Order > ()
12 // 创建用于内连接的别名customer
13 .JoinAlias(o => o.Customer, () => customer)
14 .Where(o => o.Id == id)
15 . SelectList (list => list
16 .Select(o => o.Id). WithAlias(() => dto.Id) // 给投影列取别名,用于把投影结果转成DTO
17 .Select(o => customer.Name). WithAlias (() => dto.CustomerName)
18 .Select(o => o.OrderedDateTime). WithAlias (() => dto.OrderedDateTime)
19 .Select(o => o.Amount). WithAlias (() => dto.Amount)
20 )
21 // 把投影结果转成DTO
22 .TransformUsing(Transformers.AliasToBean < OrderDTO > ())
23 .SingleOrDefault < OrderDTO > ();
24
25 transaction.Commit();
26 }
27
28 return dto;
29 }
2 {
3 OrderDTO dto = null ;
4
5 // 定义用于内连接的别名变量,该变量必须赋值为null
6 Customer customer = null ;
7
8 using (var session = NHibernateSession)
9 using (var transaction = session.BeginTransaction())
10 {
11 dto = session.QueryOver < Order > ()
12 // 创建用于内连接的别名customer
13 .JoinAlias(o => o.Customer, () => customer)
14 .Where(o => o.Id == id)
15 . SelectList (list => list
16 .Select(o => o.Id). WithAlias(() => dto.Id) // 给投影列取别名,用于把投影结果转成DTO
17 .Select(o => customer.Name). WithAlias (() => dto.CustomerName)
18 .Select(o => o.OrderedDateTime). WithAlias (() => dto.OrderedDateTime)
19 .Select(o => o.Amount). WithAlias (() => dto.Amount)
20 )
21 // 把投影结果转成DTO
22 .TransformUsing(Transformers.AliasToBean < OrderDTO > ())
23 .SingleOrDefault < OrderDTO > ();
24
25 transaction.Commit();
26 }
27
28 return dto;
29 }
输出的SQL:
同上,略
代码说明:
- SelectList()包含要投影的列;
- WithAlias()给每个投影得到的列取别名,用于投影结果转DTO;
- .TransformUsing(Transformers.AliasToBean<OrderDTO>())把投影结果转DTO。
2. 分组统计(Group)
(1)统计每个客户所有订单的总金额,以及客户Id
1
public
IEnumerable
<
CustomerIdAndTotalAmountDTO
>
GetCustomerIdAndTotalAmountDTOs()
2 {
3 CustomerIdAndTotalAmountDTO dto = null ;
4
5 IEnumerable < CustomerIdAndTotalAmountDTO > retList = null ;
6
7 using (var session = NHibernateSession)
8 using (var transaction = session.BeginTransaction())
9 {
10 retList = session.QueryOver < Order > ()
11 .SelectList(list => list
12 . SelectGroup (o => o.Customer.Id).WithAlias(() => dto.CustomerId)
13 . SelectSum (o => o.Amount).WithAlias(() => dto.TotalAmount)
14 )
15 .TransformUsing(Transformers.AliasToBean < CustomerIdAndTotalAmountDTO > ())
16 .List < CustomerIdAndTotalAmountDTO > ();
17
18 transaction.Commit();
19 }
20
21 return retList;
22 }
2 {
3 CustomerIdAndTotalAmountDTO dto = null ;
4
5 IEnumerable < CustomerIdAndTotalAmountDTO > retList = null ;
6
7 using (var session = NHibernateSession)
8 using (var transaction = session.BeginTransaction())
9 {
10 retList = session.QueryOver < Order > ()
11 .SelectList(list => list
12 . SelectGroup (o => o.Customer.Id).WithAlias(() => dto.CustomerId)
13 . SelectSum (o => o.Amount).WithAlias(() => dto.TotalAmount)
14 )
15 .TransformUsing(Transformers.AliasToBean < CustomerIdAndTotalAmountDTO > ())
16 .List < CustomerIdAndTotalAmountDTO > ();
17
18 transaction.Commit();
19 }
20
21 return retList;
22 }
输出的SQL:
NHibernate:
SELECT
this_.CustomerId
as
y0_,
sum
(this_.Amount)
as
y1_
FROM
MyWorkShop_Order this_
GROUP
BY
this_.CustomerId
代码说明:
- .SelectGroup(o => o.Customer.Id)指定分组的列;
- .SelectSum(o => o.Amount)指定对Amount调用求和聚集函数,除了SelectSum外还有SelectAvg求平均、SelectCount计数、SelectMax求最大、SelectMin求最小等常见的聚集函数。
3. 分页(Paging)
(1)分页查找
1
public
IEnumerable
<
OrderDTO
>
GetOrderDTOsByPage(
int
pageIndex,
int
pageSize)
2 {
3 OrderDTO dto = null ;
4 Customer customer = null ;
5
6 IEnumerable < OrderDTO > retList = null ;
7
8 using (var session = NHibernateSession)
9 using (var transaction = session.BeginTransaction())
10 {
11
12 retList = session.QueryOver < Order > ()
13 .JoinAlias(o => o.Customer, () => customer)
14 .SelectList(list => list
15 .Select(o => o.Id).WithAlias(() => dto.Id)
16 .Select(o => customer.Name).WithAlias(() => dto.CustomerName)
17 .Select(o => o.OrderedDateTime).WithAlias(() => dto.OrderedDateTime)
18 .Select(o => o.Amount).WithAlias(() => dto.Amount)
19 )
20 .TransformUsing(Transformers.AliasToBean < OrderDTO > ())
21 .OrderBy(o => o.Amount).Desc
22 . Skip (pageIndex * pageSize). Take (pageSize)
23 .List < OrderDTO > ();
24
25 transaction.Commit();
26 }
27
28 return retList;
29 }
2 {
3 OrderDTO dto = null ;
4 Customer customer = null ;
5
6 IEnumerable < OrderDTO > retList = null ;
7
8 using (var session = NHibernateSession)
9 using (var transaction = session.BeginTransaction())
10 {
11
12 retList = session.QueryOver < Order > ()
13 .JoinAlias(o => o.Customer, () => customer)
14 .SelectList(list => list
15 .Select(o => o.Id).WithAlias(() => dto.Id)
16 .Select(o => customer.Name).WithAlias(() => dto.CustomerName)
17 .Select(o => o.OrderedDateTime).WithAlias(() => dto.OrderedDateTime)
18 .Select(o => o.Amount).WithAlias(() => dto.Amount)
19 )
20 .TransformUsing(Transformers.AliasToBean < OrderDTO > ())
21 .OrderBy(o => o.Amount).Desc
22 . Skip (pageIndex * pageSize). Take (pageSize)
23 .List < OrderDTO > ();
24
25 transaction.Commit();
26 }
27
28 return retList;
29 }
输出的SQL:
SELECT
TOP
(
@p0
) y0_, y1_, y2_, y3_
FROM
(
SELECT
this_.Id
as
y0_, customer1_.Name
as
y1_, this_.OrderedDateTime
as
y2_, this_.Amount
as
y3_,
ROW_NUMBER()
OVER
(
ORDER
BY
this_.Amount
DESC
)
as
__hibernate_sort_row
FROM
MyWorkShop_Order this_
inner
join
MyWorkShop_Customer customer1_
on
this_.CustomerId
=
customer1_.Id)
as
query
WHERE
query.__hibernate_sort_row
>
@p1
ORDER
BY
query.__hibernate_sort_row;
@p0
=
2
[
Type: Int32 (0)
]
,
@p1
=
2
[
Type: Int32 (0)
]
代码说明:
- 调用Skip()、Take()实现数据分页读取。
1
public
int
GetOrderCount()
2 {
3 using (var session = NHibernateSession)
4 using (var transaction = session.BeginTransaction())
5 {
6 int count = session.QueryOver < Order > ()
7 . RowCount ();
8
9 transaction.Commit();
10
11 return count;
12 }
13 }
2 {
3 using (var session = NHibernateSession)
4 using (var transaction = session.BeginTransaction())
5 {
6 int count = session.QueryOver < Order > ()
7 . RowCount ();
8
9 transaction.Commit();
10
11 return count;
12 }
13 }
输出的SQL:
SELECT
count
(
*
)
as
y0_
FROM
MyWorkShop_Order this_
代码说明:
- 调用RowCount()计算数据总量。
4. 子查询(Subquery)
(1)查找金额最大的订单
1
public
Order GetMaxAmountOrder()
2 {
3 Order order = null ;
4
5 using (var session = NHibernateSession)
6 using (var transaction = session.BeginTransaction())
7 {
8 var maxAmount = NHibernate.Criterion.QueryOver.Of < Order > ()
9 .SelectList(a => a.SelectMax(o => o.Amount));
10
11 order = session.QueryOver < Order > ()
12 . WithSubquery .WhereProperty (o => o.Amount).Eq( maxAmount)
13 .SingleOrDefault();
14
15 transaction.Commit();
16 }
17 return order;
18 }
2 {
3 Order order = null ;
4
5 using (var session = NHibernateSession)
6 using (var transaction = session.BeginTransaction())
7 {
8 var maxAmount = NHibernate.Criterion.QueryOver.Of < Order > ()
9 .SelectList(a => a.SelectMax(o => o.Amount));
10
11 order = session.QueryOver < Order > ()
12 . WithSubquery .WhereProperty (o => o.Amount).Eq( maxAmount)
13 .SingleOrDefault();
14
15 transaction.Commit();
16 }
17 return order;
18 }
输出的SQL:
SELECT
this_.Id
as
Id9_0_, this_.CustomerId
as
CustomerId9_0_, this_.OrderedDateTime
as
OrderedD3_9_0_, this_.Amount
as
Amount9_0_
FROM
MyWorkShop_Order this_
WHERE
this_.Amount
=
(
SELECT
max
(this_0_.Amount)
as
y0_
FROM
MyWorkShop_Order this_0_)
代码说明:
- .WithSubquery指定子查询。