一、引言
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指定子查询。