本节内容

  • 关联查询引入
  • 一对多关联查询
    • 1.原生SQL关联查询
    • 2.HQL关联查询
    • 3.Criteria API关联查询
  • 结语

关联查询引入

在NHibernate中提供了三种查询方式给我们选择:NHibernate查询语言(HQL,NHibernate Query Language)、条件查询(Criteria API,Query By Example(QBE)是Criteria API的一种特殊情况)、原生SQL(Literal SQL,T-SQL、PL/SQL)。这一节分别使用这三种方式来关联查询。

首先看看上一篇我们为Customer和Order建立的父子关系:

一对多关联查询

1.原生SQL关联查询

在关系模型中:可以使用子表作为内连接查询Customer,像这样:

select * from Customer c inner join Order o on c.CustomerId=o.CustomerId where o.CustomerId=<id of the Customer>

使用父表作为内连接查询Order,像这样:

select * from Oder o inner join Customer c on o.CustomerId=c.CustomerId where o.OrderId=<id of the Order>

下面我们来看看在NHibernate中使用原生SQL查询。这篇来完成查询订单在orderData之后的顾客列表不同查询的写法。

public IList<Customer> UseSQL_GetCustomersWithOrders(DateTime orderDate)
{
return _session.CreateSQLQuery("select distinct {customer.*} from Customer {customer}"+
" inner join [Order] o on o.Customer={customer}.CustomerId where o.OrderDate> :orderDate")
.AddEntity("customer", typeof(Customer))
.SetDateTime("orderDate", orderDate)
.List<Customer>();
}

具体情况是:实例化IQuery接口;使用ISession.CreateSQLQuery()方法,传递的参数是SQL查询语句;{Customer.*}标记是Customer所有属性的简写。 使用AddEntity查询返回的持久化类,SetDataTime设置参数,根据不同类型,方法名不同。

2.HQL关联查询

查询订单在orderData之后的顾客列表的HQL关联查询写法:

public IList<Customer> UseHQL_GetCustomersWithOrders(DateTime orderDate)
{
return _session.CreateQuery("select distinct c from Customer c inner join c.Orders o where o.OrderDate > :orderDate")
.SetDateTime("orderDate", orderDate)
.List<Customer>();
}

这里使用基于面向对象的HQL,一目了然,符合面向对象编程习惯。

写个测试用例测试UseHQL_GetCustomersWithOrdersTest()查询方法是否正确:

[Test]
public void UseHQL_GetCustomersWithOrdersTest()
{
IList<Customer> customers = _relation.UseHQL_GetCustomersWithOrders(new DateTime(2008, 10, 1));
foreach (Customer c in customers)
{
foreach (Order o in c.Orders)
{
Assert.GreaterOrEqual(o.OrderDate, new DateTime(2008, 10, 1));
}
}
foreach (Customer c in customers)
{
Assert.AreEqual(1, customers.Count<Customer>(x => x == c));
}
}

首先调用UseHQL_GetCustomersWithOrders()方法查询订单在2008年10月1号之后的顾客列表,遍历顾客列表,断言顾客为预期的1个,他的订单时间在2008年10月1号之后。OK!测试成功。注意:这个测试用例可测试本篇所有的关联查询。

3.Criteria API关联查询

我们使用CreateCriteria()在关联之间导航,很容易地在实体之间指定约束。这里第二个CreateCriteria()返回一个ICriteria的新实例,并指向Orders实体的元素。在查询中子对象使用子CreateCriteria语句,这是因为实体之间的关联我们在映射文件中已经定义好了。还有一种方法使用CreateAlias()不会创建ICriteria的新实例。

这个例子返回顾客列表有重复的,不是我们想要的结果。

public IList<Customer> UseCriteriaAPI_GetCustomersWithOrders(DateTime orderDate)
{
return _session.CreateCriteria(typeof(Customer))
.CreateCriteria("Orders")
.Add(Restrictions.Gt("OrderDate", orderDate))
.List<Customer>();
}

预过滤

使用ICriteria接口的SetResultTransformer(IResultTransformer resultTransformer)方法返回满足特定条件的Customer。上面例子中使用条件查询,观察其生成的SQL语句并没有distinct,这时可以使用NHibernate.Transform命名空间中的方法或者使用NHibernate提供的NHibernate.CriteriaUtil.RootEntity、NHibernate.CriteriaUtil.DistinctRootEntity、NHibernate.CriteriaUtil.AliasToEntityMap静态方法实现预过滤的作用。那么上面的查询应该修改为:

public IList<Customer> UseCriteriaAPI_GetCustomersWithOrders(DateTime orderDate)
{
return _session.CreateCriteria(typeof(Customer))
.CreateCriteria("Orders")
.Add(Restrictions.Gt("OrderDate", orderDate))
.SetResultTransformer(new NHibernate.Transform.DistinctRootEntityResultTransformer())
//或者.SetResultTransformer(NHibernate.CriteriaUtil.DistinctRootEntity)
.List<Customer>();
}

这个例子从转换结果集的角度实现了我们想要的效果。

投影

调用SetProjection()方法可以实现应用投影到一个查询中。NHibernate.Criterion.Projections是Projection的实例工厂,Projections提供了非常多的方法,看看下面的截图,下拉列表中的方法是不是很多啊:

现在可以条件查询提供的投影来完成上面同样的目的:

public IList<Customer> UseCriteriaAPI_GetDistinctCustomers(DateTime orderDate)
{
IList<int> ids = _session.CreateCriteria(typeof(Customer))
.SetProjection(Projections.Distinct(Projections.ProjectionList()
.Add(Projections.Property("CustomerId"))
)
)
.CreateCriteria("Orders")
.Add(Restrictions.Gt("OrderDate", orderDate))
.List<int>(); return _session.CreateCriteria(typeof(Customer))
.Add(Restrictions.In("CustomerId", ids.ToArray<int>()))
.List<Customer>();
}

我们可以添加若干的投影到投影列表中,例如这个例子我添加一个CustomerId属性值到投影列表中,这个列表中的所有属性值都设置了Distinct投影,第一句返回订单时间在orderDate之后所有顾客Distinct的CustomerId,第二句根据返回的CustomerId查询顾客列表。达到上面的目的。这时发现其生成的SQL语句中有distinct。我们使用投影可以很容易的组合我们需要的各种方法。

[转]NHibernate之旅(10):探索父子(一对多)关联查询的更多相关文章

  1. MyBatis:一对多关联查询

    MyBatis从入门到放弃四:一对多关联查询 前言 上篇学习了一对一关联查询,这篇我们学习一对多关联查询.一对多关联查询关键点则依然是配置resultMap,在resultMap中配置collecti ...

  2. MyBatis多对一,一对多,多对多,一对多关联查询

    一.Person实体类 1 public class Person { 2 private Integer personId; 3 private String name; 4 private Int ...

  3. MyBatis初级实战之六:一对多关联查询

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  4. 7.mybatis一对多关联查询

    和第5节一对一查询类似,但是不同的是,一对一使用的是association,而一对多使用collection. 实例: 1个班级Class,对应1个老师Teacher,对应多个学生Student 1. ...

  5. MyBitis(iBitis)系列随笔之五:多表(一对多关联查询)

    MyBitis(iBitis)系列随笔之一:MyBitis入门实例 MyBitis(iBitis)系列随笔之二:类型别名(typeAliases)与表-对象映射(ORM) MyBitis(iBitis ...

  6. MyBatis从入门到放弃四:一对多关联查询

    前言 上篇学习了一对一关联查询,这篇我们学习一对多关联查询.一对多关联查询关键点则依然是配置resultMap,在resultMap中配置collection属性,别忽略了ofType属性. 搭建开发 ...

  7. MyBatis关联查询,一对多关联查询

    实体关系图,一个国家对应多个城市 一对多关联查询可用三种方式实现: 单步查询,利用collection标签为级联属性赋值: 分步查询: 利用association标签进行分步查询: 利用collect ...

  8. mybatis一对多关联查询+pagehelper->分页错误

    mybatis一对多关联查询+pagehelper->分页错误. 现象: 网上其他人遇到的类似问题:https://segmentfault.com/q/1010000009692585 解决: ...

  9. mybatis collection 一对多关联查询,单边分页的问题总结!

    若想直接通过sql实现多级关联查询表结构得有2 个必不可少的字段:id ,parentId,levelId id:主键id, parentId:父id levelId:表示第几级(表本身关联查询的时候 ...

随机推荐

  1. 开始学习requirejs+easyui的使用.

    开始学习requirejs+easyui的使用. 目录结构: |-project |-easyui01 |-js |-main.js |-index.html |-libs libs目录下放入的是ea ...

  2. 暑假集训(1)第一弹 -----士兵队列训练问题(Hdu1276)

    Description 某部队进行新兵队列训练,将新兵从一开始按顺序依次编号,并排成一行横队,训练的规则如下:从头开始一至二报数,凡报到二的出列,剩下的向小序号方向靠拢,再从头开始进行一至三报数,凡报 ...

  3. IQueryable接口与IEnumberable区别

    IEnumerable<T> 泛型类在调用自己的SKip 和 Take 等扩展方法之前数据就已经加载在本地内存里了,而IQueryable<T> 是将Skip ,take 这些 ...

  4. session绑定线程

  5. linux运维工程师,必须掌握以下几个工具

    本人是linux运维工程师,对这方面有点心得,现在我说说要掌握哪方面的工具吧说到工具,在行外可以说是技能,在行内我们一般称为工具,就是运维必须要掌握的工具.我就大概列出这几方面,这样入门就基本没问题了 ...

  6. 进程显示,删除,调度 ps, top kill

    ps:查看进程的情况,显示的是某一时间进程的运行状态.ps --help top:也是查看进程的情况,动态显示进程信息! kill:杀死进程的情况, sudo kill --help 查看相关参数 c ...

  7. Android中图片的异步加载

    转: 1.  为什么要异步加载图片 下载图片比较费时,先显示文字部分,让加载图片的过程在后台,以提升用户体验 2.  SoftReference的作用 栈内存—引用 堆内存—对象 Eg: Object ...

  8. 禁止选择文本和禁用右键 v3.0

    禁止选中字体(注:在火狐浏览器没有起到效果作用) <script> function disableselect(e) { var omitformtags = ["input& ...

  9. JS获取IP、MAC和主机名的五种方法

    javascript获取客户端IP的小程序,下面的代码是我在所有windowsNT5.0及以上的系统上都测试通过的,喜欢的朋友可以收藏下.今天在搞JS(javascript)获取客户端IP的小程序,上 ...

  10. 表格行变换顺序功能(jquery)

    周末写了个更改表格行顺序的小功能,直接贴代码 表格部分如下: <table class="table" id="test_table"> <t ...