zoukankan      html  css  js  c++  java
  • NHibernate 3 查询APIQueryOver

    NHibernate 3 查询APIQueryOver

    前言:

    NHibernate3新出了新的查询API QueryOver。本节将为您介绍这查询API QueryOver的相关内容。

    QueryOver构建在NHibernate原有的ICriteria API之上,支持Lambda表达式与扩展方法。

    下面我们将有实例项目来介绍:

    实例项目说明

    1. 项目组织结构

    • MyWorkShop.Model项目:创建实体类与DTO;
    • MyWorkShop.Data项目:创建数据访问接口;
    • MyWorkShop.Data.NHibernate项目:定义数据访问的NHibernate实现,包含文中所列举的各查询方法;
    • MyWorkShop.Data.NHibernate.Test项目:对MyWorkShop.Data.NHibernate项目中各数据访问方法进行测试。

    2. 测试数据库     

          测试数据库名为MyWorkShop,数据库文件位于db文件夹中,附加数据库文件即可;当然也可手工创建名为MyWorkShop的数据库。

    3. NHibernate配置文件

          hibernate.cfg.xml位于MyWorkShop.Data.NHibernate.Test项目中的,可根据自己的运行环境进行相应的配置。

    开发环境与工具 

          先介绍一下这个实例的开发环境与工具:
    • NHibernate 3.2 Alpha2 :下载地址 http://sourceforge.net/projects/nhibernate/files/NHibernate/3.2.0Alpha2/
    • Visual Studio 2010
    • .NET Framework 4.0
    • NUnit 2.5.10
    • SQL Server 2005

    实例场景

          为便于理解与掌握,举个故意简化的实例:客户(Customer)与订单(Order),一个客户可以下多个订单。实体类的代码如下: 

    (1)实体基类: Entity

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 
     6 namespace MyWorkShop.Model.Entities
     7 {
     8     public abstract class Entity<TId>
     9     {
    10         public virtual TId Id { get; protected set; }
    11     }
    12 }

      (2)客户类:Customer,映射的数据表为MyWorkShop_Customer

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 
     6 namespace MyWorkShop.Model.Entities
     7 {
     8     public class Customer:Entity<int>
     9     {
    10         public virtual string Name { get; set; }
    11         public virtual string Address { get; set; }
    12         public virtual string Phone { get; set; }
    13     }
    14 }

     (3)订单类:Order,映射的数据表为MyWorkShop_Order

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 
     6 namespace MyWorkShop.Model.Entities
     7 {
     8     //Guid做主键
     9     public class Order : Entity<Guid>
    10     {
    11         //下单客户
    12         public virtual Customer Customer { get; set; }
    13         //下单时间
    14         public virtual DateTime OrderedDateTime { get; set; }
    15         //订单金额
    16         public virtual Decimal? Amount { get; set; } 
    17     }
    18 }

    查询场景

     1. 筛选数据(Restriction)

     (1)根据客户姓名查找客户,假设客户姓名唯一

     1 public Customer GetByName(string customerName)
     2 {
     3     Customer entity = null;
     4             
     5     using (var session = NHibernateSession)
     6     using (var transaction = session.BeginTransaction())
     7     {
     8         
     9         entity = session.QueryOver<Customer>()
    10             .Where(c => c.Name == customerName)
    11             .SingleOrDefault();
    12 
    13         transaction.Commit();
    14     }
    15     return entity;
    16 }

    输出的SQL: 

    SELECT this_.Id as Id5_0_, this_.Name as Name5_0_, this_.Address as Address5_0_, this_.Phone as Phone5_0_ FROM MyWorkShop_Customer this_ WHERE this_.Name = @p0;@p0 = 'Name' [Type: String (50)]

    代码说明:

    • 筛选条件调用Where方法,使用Lambda表达式“c => c.Name == customerName”,这样就消除了ICriteria的字段名字符串硬编码的问题;
    • 返回单个值调用SingleOrDefault(),若查询结果不唯一则抛出异常NHibernate.NonUniqueResultException。

    (2)根据客户地址查找多个客户

     1 public IEnumerable<Customer> GetByAddress(string address)
     2 {
     3     IEnumerable<Customer> list = null;
     4 
     5     using (var session = NHibernateSession)
     6     using (var transaction = session.BeginTransaction())
     7     {
     8         list = session.QueryOver<Customer>()
     9             .Where(c => c.Address == address)
    10             .List();
    11 
    12         transaction.Commit();
    13     }
    14 
    15     return list;
    16 }

     输出的SQL: 

    SELECT this_.Id as Id5_0_, this_.Name as Name5_0_, this_.Address as Address5_0_, this_.Phone as Phone5_0_ FROM MyWorkShop_Customer this_ WHERE this_.Address = @p0;@p0 = 'Address' [Type: String (100)]

    代码说明:

    •  查询多条数据调用List()方法。

     (3)根据客户姓名模糊查找客户 

     1 public IEnumerable<Customer> GetByLikeName(string likeName)
     2 {
     3     IEnumerable<Customer> list = null;
     4 
     5     using (var session = NHibernateSession)
     6     using (var transaction = session.BeginTransaction())
     7     {
     8         list = session.QueryOver<Customer>()
     9             .WhereRestrictionOn(o => o.Name).IsLike(likeName, MatchMode.Anywhere)
    10             .List();
    11 
    12         transaction.Commit();
    13     }
    14 
    15     return list;
    16 }

    输出的SQL:

    SELECT this_.Id as Id5_0_, this_.Name as Name5_0_, this_.Address as Address5_0_, this_.Phone as Phone5_0_ FROM MyWorkShop_Customer this_ WHERE this_.Name like @p0;@p0 = '%e%' [Type: String (50)]

     代码说明:

    •  对于某些SQL函数与操作符(比如like、between...and...),没有直接对应的Lambda表达式,需要先使用WhereRestrictionOn方法指定筛选条件的列,然后再调用相应的方法指定筛选条件;
    • IsLike方法指定字符串匹配查找。

     (4)查找金额在指定范围内的订单 

    2 public IEnumerable<Order> GetByAmount(decimal minAmount, decimal maxAmount)
     3 {
     4     IEnumerable<Order> list = null;
     5 
     6     using (var session = NHibernateSession)
     7     using (var transaction = session.BeginTransaction())
     8     {
     9         list = session.QueryOver<Order>()
    10             .Where(o => o.Amount >= minAmount)
    11             .And(o => o.Amount <= maxAmount)
    12             .OrderBy(o => o.Amount).Desc
    13             .List();
    14 
    15         transaction.Commit();
    16     }
    17 
    18     return list;
    19 }

     输出的SQL: 

    SELECT this_.Id as Id8_0_, this_.CustomerId as CustomerId8_0_, this_.OrderedDateTime as OrderedD3_8_0_, this_.Amount as Amount8_0_ FROM MyWorkShop_Order this_ WHERE this_.Amount >= @p0 and this_.Amount <= @p1 ORDER BY this_.Amount desc;@p0 = 100 [Type: Decimal (0)], @p1 = 200 [Type: Decimal (0)]

     代码说明:

    • 多个条件可使用Where...And...逐个指定,也可以在一个Where方法中指定,比如上面的条件可以写成Where(o => o.Amount >= minAmount && o.Amount <= maxAmount);
    • 排序使用OrderBy,升序降序使用Asc与Desc。

    2.连接(Join)

    (1)内连接:根据客户姓名查找订单 

     1 public IEnumerable<Order> GetByCustomerName(string customerName)
     2 {
     3     IEnumerable<Order> list = null;
     4 
     5     using (var session = NHibernateSession)
     6     using (var transaction = session.BeginTransaction())
     7     {
     8         list = session.QueryOver<Order>()
     9             .OrderBy(o=>o.Amount).Desc
    10             .Inner.JoinQueryOver<Customer>(o => o.Customer)
    11             .Where(c => c.Name == customerName)            
    12             .List();
    13 
    14         transaction.Commit();
    15     }
    16 
    17     return list;
    18 }

     输出的SQL: 

    SELECT this_.Id as Id8_1_, this_.CustomerId as CustomerId8_1_, this_.OrderedDateTime as OrderedD3_8_1_, this_.Amount as Amount8_1_, customer1_.Id as Id9_0_, customer1_.Name as Name9_0_, customer1_.Address as Address9_0_, customer1_.Phone as Phone9_0_ FROM MyWorkShop_Order this_ inner join MyWorkShop_Customer customer1_ on this_.CustomerId=customer1_.Id WHERE customer1_.Name = @p0 ORDER BY this_.Amount desc;@p0 = 'Name' [Type: String (50)]

     代码说明: 

    • .Inner.JoinQueryOver指定内连接,如果省略Inner仅写JoinQueryOver默认就是内连接;
    • .Left、.Right则分别为左外连接、右外连接
    (2)使用别名进行内连接:根据客户姓名查找订单
     1 public IEnumerable<Order> GetByCustomerNameViaAlias(string customerName)
     2 {
     3     //定义用于内连接的别名变量,该变量必须赋值为null
     4     Customer customer = null;
     5 
     6     IEnumerable<Order> list = null;
     7 
     8     using (var session = NHibernateSession)
     9     using (var transaction = session.BeginTransaction())
    10     {
    11         list = session.QueryOver<Order>()
    12             .JoinAlias(o => o.Customer, () => customer) //指定别名customer
    13             .Where(() => customer.Name == customerName)
    14             .List();
    15 
    16         transaction.Commit();
    17     }
    18 
    19     return list;
    20 }
     输出的SQL:

           同上,略

     代码说明:
    •  可以通过.Inner.JoinQueryOver来显式进行内连接,也可以通过.JoinAlias创建连接别名进行连接;
    •  连接别名变量在QueryOver使用之前定义,并且必须赋null值。

    小结     

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

          与ICriteria API相比,个人认为QueryOver并不见得会提高代码的可读性,但QueryOver解决了

    ICriteria API字符串硬编码的问题,从而减少代码输入的错误,大大提高了代码重构的能力,因此用QueryOver取代ICriteria API是值得的。

  • 相关阅读:
    C#(winform)浏览按钮
    C#操作CSV存取类
    忘记windows的登陆密码
    exe文件打开方式(恢复EXE文件关联)
    MongoDB 搭建副本集
    Performance Monitor3:监控SQL Server的内存压力
    大数据操作:删除和去重
    SSRS配置2:加密管理
    SSISDB7:当前正在运行的Package及其Executable
    不再迷惑,无值和NULL值
  • 原文地址:https://www.cnblogs.com/wangyhua/p/4050539.html
Copyright © 2011-2022 走看看