摘要
上一篇文章介绍了NHibernate HQL,他的缺点是不能够在编译时发现问题。如果数据库表结构有改动引起了实体关系映射的类有改动,要同时修改这些HQL字符串。这篇文章介绍NHibernate面向对象的Criteria查询。他提供了很多API接口,可以实现“私人订制”式的面向对象查询。
作为对照,本篇文章将上篇文章的八个HQL查询全部用Criteria查询写一遍,读者可以自己感受下他们之间的区别。
本篇文章的代码可以到NHibernate查询下载
1、创建Criteria对象,查询所有Customer对象信息
1 public IList<Customer> QueryAllCriteria() 2 { 3 return Session.CreateCriteria<Customer>().List<Customer>(); 4 }
ISession.CreateCriteria方法返回持久化查询对象的ICriteria实例,他是使用Criteria查询的第一步。传入一个类型参数,表示是对哪一个实体类持久化对象的查询实例。List方法返回持久化类的所有对象。
2、CreateAlias方法创建别名
1 public IList<Customer> QueryAllCriteria() 2 { 3 return Session.CreateCriteria<Customer>() 4 .CreateAlias("Customer", "c").List<Customer>(); 5 }
3、指定对象返回数组
1 public IList<int> SelectIdCriteria() 2 { 3 IList<int> ids = Session.CreateCriteria(typeof(Customer)) 4 .SetProjection(Projections.Distinct(Projections.ProjectionList().Add(Projections.Property("Id")))) 5 .List<int>(); 6 return ids; 7 }
ICriteria.SetProjection方法将应用投影到查询中,Projections有很多静态方法生成Distinct、GroupBy、Max、Min、Avg、Sum等投影。
4、添加查询条件
1 public IList<Customer> GetCustomerByNameCriteria(string firstName, string lastName) 2 { 3 return Session.CreateCriteria<Customer>() 4 .Add(Restrictions.And( 5 Restrictions.Eq("FirstName", firstName), Restrictions.Eq("LastName", lastName)) 6 ).List<Customer>(); 7 } 8 9 public IList<Customer> GetCustomersStartWithCriteria() 10 { 11 var list = Session.CreateCriteria<Customer>().Add(Restrictions.Like("FirstName", "J%")).List<Customer>(); 12 return list.ToList(); 13 }
ICriteria.Add方法用来添加where条件,Restrictions类提供了很多生成查询条件的API方法。
Restrictions.And方法和Restrictions.Or方法对应SQL的and和or条件,可以传入多个条件对象拼接and/or子句,可以灵活地使用括号进行分组。
示例中的Restrictions.Eq方法和Restrictions.Like方法是最常用的Restrictions方法API。
此外,Restrictions还提供Between、Ge、Gt、Le、Lt、In、IsNotNull、IsNull、Where等多个API方法,可以满足生成大部分查询条件。
5、order by
1 public IList<Customer> GetCustomersOrderByCriteria() 2 { 3 var list = Session.CreateCriteria<Customer>().AddOrder(new NHibernate.Criterion.Order("FirstName", true)).List<Customer>(); 4 return list.ToList(); 5 }
ICriteria.AddOrder方法添加order子句,传入Criteria.Order对象。Criteria.Order类构造函数传入两个参数,第一个参数表示对哪个属性进行排序,第二个字段表示是升序还是降序,true是升序,false是降序。
6、关联查询
1)按分组查询客户Id及客户关联的订单数量
1 public IList<object[]> SelectOrderCountCriteria() 2 { 3 var query = Session.CreateCriteria(typeof(Customer)).CreateCriteria("Orders") 4 .SetProjection(Projections.ProjectionList() 5 .Add(Projections.GroupProperty("Id")) 6 .Add(Projections.RowCount())); 7 return query.List<object[]>(); 8 }
对查询进行分组通用的代码是:
var query = Session.CreateCriteria(typeof(类名)) .SetProjection(Projections.ProjectionList() .Add(Projections.GroupProperty("属性名")) .Add(Projections.RowCount() | Projections.Max("Max投影属性名") | Projections.Sum("Sum投影属性名")));
因为要对Order记录进行分组统计,所以使用CreateCriteria("Orders")创建与Orders的关联。
2)查询在指定日期到当前内所有下订单客户信息
1 public IList<Customer> GetCustomersOrderDateGreatThanCriteria(DateTime orderDate) 2 { 3 return Session.CreateCriteria<Customer>().CreateCriteria("Orders").Add(Restrictions.Gt("Ordered", orderDate)) 4 .SetResultTransformer(new NHibernate.Transform.DistinctRootEntityResultTransformer()) 5 .List<Customer>(); 6 }
ICriteria.SetResultTransformer(new NHibernate.Transform.DistinctRootEntityResultTransformer())的结果相当于对HQL查询使用了distinct子句。
3)查询所有订单数量大于2的客户信息
1 public IList<Customer> GetCustomersOrderCountGreaterThanCriteria() 2 { 3 var query = Session.CreateCriteria(typeof(Customer)).CreateCriteria("Orders") 4 .SetProjection(Projections.ProjectionList() 5 .Add(Projections.GroupProperty("Id")) 6 .Add(Projections.RowCount())); 7 IList<object[]> groups = query.List<object[]>(); 8 9 IList<int> ids = groups.Where(g => (int)g[1] > 2).Select(g => (int)g[0]).ToList(); 10 11 return Session.CreateCriteria(typeof(Customer)) 12 .Add(Restrictions.In("Id", ids.ToArray<int>())) 13 .List<Customer>(); 14 }
因为ICriteria目前不支持对Group分组结果进行Having筛选,因此必须先对Customer和Orders的关联查询以Customer.Id进行分组,得到Customer.Id和Order数量作为属性的对象的集合,对这个集合执行linq查询,得到订单数量大于2的客户Id集合,最后用Restrictions.In方法对Customer对象集合进行筛选,选出Customer.Id在订单数量大于2的客户Id集合内的客户信息。这样就能够实现Having的查询功能。
结语
NHibernate Criteria查询优点是完全面向对象,ICriteria对象提供了大量的API函数实现完全面向对象的查询语句。可以在编译时发现问题。在更改实体类属性后(如果改动不大),不怎么需要修改查询语句。缺点是功能有限,写复杂的分组查询和条件查询比较困难。一般情况下,在写简单查询的时候用Criteria查询,在写复杂查询的时候使用HQL或者Linq to NHibernate。