Using Criteria Queries
使用条件查询
在上一章中,通过实体的ID来获取这些实体.本节介绍几个基础的条件查询:通过实体的属性来获取实体.
步骤
1. 完成本章简介中的通用步骤.
2. 在Queries类中,添加下面的方法:public IEnumerableGetMoviesDirectedBy(string directorName){ return _session.CreateCriteria () .Add(Restrictions.Eq("Director", directorName)) .List ();}
3. 在Queries类中,添加下面的方法,该方法通过actor name来查询movies:
public IEnumerableGetMoviesWith(string actorName){ return _session.CreateCriteria () .CreateCriteria("Actors", JoinType.InnerJoin) .Add(Restrictions.Eq("Actor", actorName)) .List ();}
4. 在Queries类中,添加下面的方法,该方法通过ISBN来查询book:
public Book GetBookByISBN(string isbn){ return _session.CreateCriteria() .Add(Restrictions.Eq("ISBN", isbn)) .UniqueResult ();}
5. 添加下面的方法,该方法查找一定价格范围内的所有产品:
public IEnumerableGetProductByPrice( decimal minPrice, decimal maxPrice){ return _session.CreateCriteria () .Add(Restrictions.And( Restrictions.Ge("UnitPrice", minPrice), Restrictions.Le("UnitPrice", maxPrice) )) .AddOrder(Order.Asc("UnitPrice")) .List ();}
6. 在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));}
7. 编译运行,结果如下图所示:
原理
让我们来逐一地来查看这四个查询:
- GetMoviesDirectedBy 查询
_session.CreateCriteria() .Add(Restrictions.Eq("Director", directorName)) .List ();
上面的代码中,使用session.CreateCriteria获得了一个ICriteria对象.泛型参数Movie告诉NHibernate即将在movies上进行查询.代码第二行,movies约束条件为:由Steven Spielberg执导.最后,调用List方法,该方法执行查询并返回由Steven Spielberg执导的movies.由于泛型参数Movie,NHibernate返回了一个强类型IList<Movie>,而不是IList.
在Microsoft SQL Server中, 生成的SQL语句如下:SELECT this_.Id as Id1_0_, this_.Name as Name1_0_, this_.Description as Descript4_1_0_, this_.UnitPrice as UnitPrice1_0_, this_.Director as Director1_0_FROM Product this_WHERE this_.ProductType = 'Eg.Core.Movie' AND this_.Director = 'Steven Spielberg' /* @p0 */
- GetMoviesWith 查询
_session.CreateCriteria() .CreateCriteria("Actors", JoinType.InnerJoin) .Add(Restrictions.Eq("Actor", actorName)) .List ();
我们再一次对movies进行查询,但是在这个示例中,我们基于一个子集合来进行查询.我们想得到Morgan Freeman的所有movies.依据我们的模型,返回所有对应的ActorRole对象中Actor为Morgan Freeman的movies.
代码第二行,基于Movie的Actors集合,在Movies和ActorRoles上设置了一个inner join查询.在SQL中inner join查询只返回匹配的行.CreateCriteria可以改变从Movie到ActorRole的查询结果,这使得我们可以进一步筛选ActorRoles(在代码第三行) . 代码第三行,我们只是筛选ActorRole对象,直至只有Morgan Freeman的角色.由于是inner join查询,同样也会筛选Movies.最后执行该查询并调用List<Movie>得到结果. 在Microsoft SQL Server中, 生成的SQL语句如下:SELECT this_.Id as Id1_1_, this_.Version as Version1_1_, this_.Name as Name1_1_, this_.Description as Descript5_1_1_, this_.UnitPrice as UnitPrice1_1_, this_.Director as Director1_1_, actorrole1_.Id as Id0_0_, actorrole1_.Version as Version0_0_, actorrole1_.Actor as Actor0_0_, actorrole1_.Role as Role0_0_FROM Product this_ inner join ActorRole actorrole1_ on this_.Id = actorrole1_.MovieIdWHERE this_.ProductType = 'Eg.Core.Movie' AND actorrole1_.Actor = 'Morgan Freeman' /* @p0 */
- GetBookByISBN 查询
_session.CreateCriteria() .Add(Restrictions.Eq("ISBN", isbn)) .UniqueResult ();
在这个条件查询中,通过ISBN来查询特定的book.由于使用UniqueResult<Book>替代了List<Book>,NHibernate将返回一个单独的Book对象,如果查询结果为空,将返回null.该查询假的ISBN是唯一的.
在Microsoft SQL Server中, 生成的SQL语句如下:SELECT this_.Id as Id1_0_, this_.Name as Name1_0_, this_.Description as Descript4_1_0_, this_.UnitPrice as UnitPrice1_0_, this_.Author as Author1_0_, this_.ISBN as ISBN1_0_FROM Product this_WHERE this_.ProductType = 'Eg.Core.Book' AND this_.ISBN = '3043' /* @p0 */
- GetProductByPrice 查询
_session.CreateCriteria() .Add(Restrictions.And( Restrictions.Ge("UnitPrice", minPrice), Restrictions.Le("UnitPrice", maxPrice) )) .AddOrder(Order.Asc("UnitPrice")) .List ()
使用这个条件查询,使用一个And操作符将大于等于操作和小于等于操作结合起来,以返回在定价在两个值之间的产品.And约束需要两个子约束作为参数.也可以使用Between约束来达到相同的目的,代码如下:
.Add(Restrictions.Between("UnitPrice", minPrice, maxPrice))
使用AddOrder方法,按照单价对product结果进行升序排序.
在Microsoft SQL Server中, 生成的SQL语句如下:
SELECT this_.Id as Id1_0_, this_.Name as Name1_0_, this_.Description as Descript4_1_0_, this_.UnitPrice as UnitPrice1_0_, this_.Director as Director1_0_, this_.Author as Author1_0_, this_.ISBN as ISBN1_0_, this_.ProductType as ProductT2_1_0_ FROM Product this_WHERE (this_.UnitPrice >= 0 /* @p0 */ and this_.UnitPrice <= 15 /* @p1 */)ORDER BY this_.UnitPrice asc
扩展
这些条件API是为了动态的创建查询,就像我们在许多零售网站上看到的高级搜索功能.这些网站上,用户可以选择任意数量的筛选和排序条件.然而,这些查询必须动态解析和编译.
相对于使用一组参数的静态查询,使用命名HQL查询将更好,因为她会在我们创建会话工厂的时候预编译 这些条件API存在"魔法字符串"的问题,在这些字符串涉及到程序中属性和类的地方.通过使用Visual Studio的重构工具或者ReSharper,我们可以使用强类型来轻松改变属性的名字. 使用这些条件API时,如果我们变更了模型中的一个属性名,那我们将必须更新所有使用该属性的所有条件查询.但是,在下一小节中,新的QueryOver API将解决该问题.