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)


浙公网安备 33010602011771号