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
}
(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
}
输出的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
代码说明:
- 代码中的第一个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。
(2)根据订单号查找订单,并用NHibernate内置方法转成OrderDTO
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
}
代码说明:
- SelectList()包含要投影的列;
- WithAlias()给每个投影得到的列取别名,用于投影结果转DTO;
- .TransformUsing(Transformers.AliasToBean<OrderDTO>())把投影结果转DTO。
(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
}
输出的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
}
输出的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)
]
代码说明:
(2)计算数据总量
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
}
输出的SQL:
SELECT
count
(
*
)
as
y0_
FROM
MyWorkShop_Order this_
代码说明:
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
}
输出的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_)
代码说明:
五、总结
本文与上一篇文章通过一个简单的实例,介绍了QueryOver进行
条件筛选(Restriction)、连接(Join)、投影(Projection)、把投影结果转成DTO、分页、子查询(Subquery)
等常见场景应用。
见《NHibernate 3.x新功能实践(一) QueryOver(上)》文章最后。