迷恋弦哥

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

Using the Hibernate Query Language

使用Hibernate查询语言(HQL)

  到目前为止,我们已经讨论了使用NHibernate的Criteria API和她的新的QueryOver语法的各种查询.NHibernate提供了另外一个更强大的方法,名为Hibernate Query Language,一个融合了熟悉的sql样式的语法与面向对象思想的特定于领域的语言.本节介绍如何使用HQL完成相同的查询.

步骤

1.   完成本章简介中的通用步骤.将新建的控制台程序命名为HQLExample.
2.   添加名为NameAndPrice.hbm.xml的映射文档,别忘了将其设置为嵌入式资源,代码如下:

View Code
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
    assembly="HQLExample"
    namespace="HQLExample">
  <import class="NameAndPrice"/>
</hibernate-mapping>

3.   在App.config中,在<mapping assembly="Eg.Core"/>元素下面添加<mapping assembly="HQLExample"/>.
4.   在Queries类中,添加下面的方法:

View Code
public IEnumerable<Movie> GetMoviesDirectedBy(string directorName)
{
  var hql = @"from Movie m
              where m.Director = :director";
  return _session.CreateQuery(hql)
    .SetString("director", directorName)
    .List<Movie>();
}
public IEnumerable<Movie> GetMoviesWith(string actorName)
{
  var hql = @"select m
              from Movie m
              inner join m.Actors as ar
              where ar.Actor = :actorName";
  return _session.CreateQuery(hql)
    .SetString("actorName", actorName)
    .List<Movie>();
}
public Book GetBookByISBN(string isbn)
{
  var hql = @"from Book b
              where b.ISBN = :isbn";
  return _session.CreateQuery(hql)
    .SetString("isbn", isbn)
    .UniqueResult<Book>();
}
public IEnumerable<Product> GetProductByPrice(
  decimal minPrice,
  decimal maxPrice)
{
  var hql = @"from Product p
              where p.UnitPrice >= :minPrice
              and p.UnitPrice <= :maxPrice
              order by p.UnitPrice asc";
  return _session.CreateQuery(hql)
    .SetDecimal("minPrice", minPrice)
    .SetDecimal("maxPrice", maxPrice)
    .List<Product>();
}
public IEnumerable<NameAndPrice> GetMoviePriceList()
{
  var hql = @"select new NameAndPrice(
              m.Name, m.UnitPrice)
              from Movie m";
  return _session.CreateQuery(hql)
    .List<NameAndPrice>();
}
public decimal GetAverageMoviePrice()
{
  var hql = @"select Cast(avg(m.UnitPrice)
              as Currency)
              from Movie m";
  return _session.CreateQuery(hql)
    .UniqueResult<decimal>();
}
public IEnumerable<NameAndPrice> GetAvgDirectorPrice()
{
  var hql = @"select new NameAndPrice(
                m.Director,
                Cast(avg(m.UnitPrice) as Currency)
              )
              from Movie m
              group by m.Director";
  return _session.CreateQuery(hql)
    .List<NameAndPrice>();
}

5.  在Program.cs中, 为RunQueries方法添加下述代码:

View Code
static void RunQueries(ISession session)
{
  var queries = new Queries(session);
  Show("Movies directed by Spielberg:",
    queries.GetMoviesDirectedBy(
    "Steven Spielberg"));
  Show("Movies with Morgan Freeman:",
    queries.GetMoviesWith(
    "Morgan Freeman"));
  Show("This book:",
    queries.GetBookByISBN(
    "978-1-849513-04-3"));
  Show("Cheap products:",
    queries.GetProductByPrice(0M, 15M));
  Show("Movie Price List:",
    queries.GetMoviePriceList());
  Show("Average Movie Price:",
    queries.GetAverageMoviePrice());
  Show("Average Price by Director:",
    queries.GetAvgDirectorPrice());
}

6.   编译运行,结果如下图所示:

原理

  HQL在语法和SQL的非常相像,但是操作在一个对象级别.我们将所有的查询构建为字符串.很像ADO.NET中的DbCommands,我们围绕这些查询字符串来创建IQuery对象,设置参数值,并且使用List和UniqueResult来执行查询.与Microsoft SQL Server查询中使用"@"类似,在HQL的查询字符串中,我们使用":"来标记参数.当我们设置参数值的时候,不需要包含":".

  • GetMoviesDirectedBy 查询

  有这样一个非常基础的HQL查询:

from Movie m
where m.Director = :director

  为使代码简洁,为movies取了一个简单的别名m.在这种情况下,有一个隐式的select m语句来投影movies.我们有一个单独的参数director,我们使用她来筛选movies.

  • GetMoviesWith 查询
View Code
select m
from Movie m
inner join m.Actors as ar
where ar.Actor = :actorName

  在这个查询中,使用了movies到ActorRoles的联接.注意和SQL的不同之处,我们不需要指定ActorRoles,或是明确使用一个比较式的ON子句.NHibernate已经知道了实体间的关系.我们使用actor的name来筛选actor.就像SQL,因为使用了一个inner join(内联接)查询,actor role上的筛选也会影响到movies.

  • GetProductByPrice 查询
View Code
from Product p
where p.UnitPrice >= :minPrice
and p.UnitPrice <= :maxPrice
order by p.UnitPrice asc

  在这个查询中,使用了两个参数(minPrice和maxPrice)划定了一个价格范围,使用该价格范围来筛选Product.这个查询使用HQL的between来写,代码如下:

View Code
from Product p
where p.UnitPrice between
:minPrice and :maxPrice
order by p.UnitPrice asc

  和SQL一样,order by子句依照单价对products排序.

  • GetMoviePriceList 查询

  有这样的一个简单查询:

select new NameAndPrice(m.Name, m.UnitPrice)
from Movie m

  使用HQL时,要关注对象和属性,而不是数据库表和列.该查询将Name属性和UnitPrice属性注入到NameAndPrice类的构造函数中:
public NameAndPrice(string name, decimal unitPrice)
  然后投影产生NameAndPrice的实例.为了使NHibernate知道该类的存在,我们使用了下面的导入映射(import mapping):
<import class="NameAndPrice"/>
  作为一个选择方案,如同criteria和QueryOver一样,我们可以简单的投射Name和UnitPrice,返回一个对象数组list,然后使用LINQ to Objects将她们转换NameAndPrice实例,代码如下:

View Code
var hql = @"select m.Name, m.UnitPrice
            from Movie m";
var query = session.CreateQuery(hql);
return query.List<object[]>()
  .Select(props =>
    new NameAndPrice(
      (string)props[0],
      (decimal)props[1]));

  这种情况下,不需要导入NameAndPrice类.

  • GetAverageMoviePrice 查询
select Cast(avg(m.UnitPrice) as Currency)
from Movie m

  这个查询中,使用了聚合函数average.返回一个double类型的标量值,所以我们将它转换回NHibernate的Currency类型.在.NET中与之等价类型是decimal,,因此我们使用UniqueResult<decimal>()来执行该查询.

  • GetAvgDirectorPrice 查询
View Code
select new NameAndPrice(
    m.Director,
    Cast(avg(m.UnitPrice) as Currency)
)
from Movie m
group by m.Director

  在这个查询中,按照Director来分组.然后将Director 和UnitPrice的平均值注入到NameAndPrice的构造函数中,和刚才一样,由于avg 返回的是double类型,因此需要先将她转换回Currency.

扩展

  除了在实体上映射属性和集合,HQL还允许我们在两个特殊的隐含属性上进行查询:

  • 属性类是实体类型的全名,例如,查询books时,可以使用这样的代码:
from Product p where p.class='Eg.Core.Book'
  • 属性id通常代表实体的POID,不管在实体中我们怎么称呼她.我们都可以使用下面的代码来一次查询3本书:
from Book b where b.id in (@id0, @id1, @id2)
posted on 2012-07-26 14:36  迷恋弦哥  阅读(283)  评论(0)    收藏  举报