Using the Hibernate Query Language
使用Hibernate查询语言(HQL)
到目前为止,我们已经讨论了使用NHibernate的Criteria API和她的新的QueryOver语法的各种查询.NHibernate提供了另外一个更强大的方法,名为Hibernate Query Language,一个融合了熟悉的sql样式的语法与面向对象思想的特定于领域的语言.本节介绍如何使用HQL完成相同的查询.
步骤
1. 完成本章简介中的通用步骤.将新建的控制台程序命名为HQLExample.
2. 添加名为NameAndPrice.hbm.xml的映射文档,别忘了将其设置为嵌入式资源,代码如下:3. 在App.config中,在<mapping assembly="Eg.Core"/>元素下面添加<mapping assembly="HQLExample"/>.
4. 在Queries类中,添加下面的方法:public IEnumerableGetMoviesDirectedBy(string directorName){ var hql = @"from Movie m where m.Director = :director"; return _session.CreateQuery(hql) .SetString("director", directorName) .List ();}public IEnumerable 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 ();}public Book GetBookByISBN(string isbn){ var hql = @"from Book b where b.ISBN = :isbn"; return _session.CreateQuery(hql) .SetString("isbn", isbn) .UniqueResult ();}public IEnumerable 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 ();}public IEnumerable GetMoviePriceList(){ var hql = @"select new NameAndPrice( m.Name, m.UnitPrice) from Movie m"; return _session.CreateQuery(hql) .List ();}public decimal GetAverageMoviePrice(){ var hql = @"select Cast(avg(m.UnitPrice) as Currency) from Movie m"; return _session.CreateQuery(hql) .UniqueResult ();}public IEnumerable 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 ();}
5. 在Program.cs中, 为RunQueries方法添加下述代码:
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 mwhere m.Director = :director
为使代码简洁,为movies取了一个简单的别名m.在这种情况下,有一个隐式的select m语句来投影movies.我们有一个单独的参数director,我们使用她来筛选movies.
- GetMoviesWith 查询
select mfrom Movie minner join m.Actors as arwhere ar.Actor = :actorName
在这个查询中,使用了movies到ActorRoles的联接.注意和SQL的不同之处,我们不需要指定ActorRoles,或是明确使用一个比较式的ON子句.NHibernate已经知道了实体间的关系.我们使用actor的name来筛选actor.就像SQL,因为使用了一个inner join(内联接)查询,actor role上的筛选也会影响到movies.
- GetProductByPrice 查询
from Product pwhere p.UnitPrice >= :minPriceand p.UnitPrice <= :maxPriceorder by p.UnitPrice asc
在这个查询中,使用了两个参数(minPrice和maxPrice)划定了一个价格范围,使用该价格范围来筛选Product.这个查询使用HQL的between来写,代码如下:
from Product pwhere p.UnitPrice between:minPrice and :maxPriceorder 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实例,代码如下:var hql = @"select m.Name, m.UnitPrice from Movie m";var query = session.CreateQuery(hql);return query.List
这种情况下,不需要导入NameAndPrice类.
- GetAverageMoviePrice 查询
select Cast(avg(m.UnitPrice) as Currency)from Movie m
这个查询中,使用了聚合函数average.返回一个double类型的标量值,所以我们将它转换回NHibernate的Currency类型.在.NET中与之等价类型是decimal,,因此我们使用UniqueResult<decimal>()来执行该查询.
- GetAvgDirectorPrice 查询
select new NameAndPrice( m.Director, Cast(avg(m.UnitPrice) as Currency))from Movie mgroup 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)