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 {  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.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  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()实现数据分页读取。
(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(上)》文章最后。

你可能感兴趣的:(Hibernate)