NHibernate 3.x新功能实践(二) QueryOver(下)

一、引言

       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 { getset; }
11         public string CustomerName getset; }
12         public DateTime OrderedDateTime { getset; }
13         public Decimal? Amount { getset; } 
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.Idcustomer.Nameo.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 }
复制代码
输出的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 }
复制代码

  输出的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 DESCas __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()实现数据分页读取。
(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_

 代码说明:

  •  调用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 }
复制代码

输出的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指定子查询。 

五、总结 

      本文与上一篇文章通过一个简单的实例,介绍了QueryOver进行条件筛选(Restriction)、连接(Join)、投影(Projection)、把投影结果转成DTO、分页、子查询(Subquery)等常见场景应用。 

六、参考资料

       见《NHibernate 3.x新功能实践(一) QueryOver(上)》文章最后。

七、实例源代码下载

       见《NHibernate 3.x新功能实践(一) QueryOver(上)》文章最后。

posted @ 2014-03-02 23:19  王永华  阅读(208)  评论(0编辑  收藏  举报