目录

写在前面

文档与系列文章

多对多关系关联查询

总结

写在前面

上篇文章介绍了nhibernate中对一对多关系进行关联查询的几种方式,以及在使用过程需要注意的问题。这篇文章对多对多关系的查询处理也采用上篇文章的描述方式进行说明。

文档与系列文章

[Nhibernate]体系结构

[NHibernate]ISessionFactory配置

[NHibernate]持久化类(Persistent Classes)

[NHibernate]O/R Mapping基础

[NHibernate]集合类(Collections)映射 

[NHibernate]关联映射

[NHibernate]Parent/Child

[NHibernate]缓存(NHibernate.Caches)

[NHibernate]NHibernate.Tool.hbm2net

[NHibernate]Nullables

[NHibernate]Nhibernate如何映射sqlserver中image字段

[NHibernate]基本配置与测试 

[NHibernate]HQL查询 

[NHibernate]条件查询Criteria Query

[NHibernate]增删改操作

[NHibernate]事务

[NHibernate]并发控制

[NHibernate]组件之依赖对象

[NHibernate]一对多关系(级联删除,级联添加)

[NHibernate]一对多关系(关联查询)

多对多关系关联查询

多对多关系非常常见,比如系统中常用的权限管理问题,一个用户有多个权限,当然一个权限可以属于多个用户(这样描述只是为了对多对多关系有个感性的认识)。那么在咱们的客户/订单/产品,这三张表中有没有多对多的关系呢?

订单可以有多个产品,一种产品可以属于多个订单(这个地方有点绕,如果再有一个数量的字段,比较好理解,下一个订单,只是库存减少了,但id还是那个id)。关系图如下:

其中表TB_OrderProduct为order和product的关系表,字段OrderID和ProductID为联合主键。

添加联合主键的sql语句如下:

 ALTER TABLE tb_orderproduct WITH NOCHECK ADD
CONSTRAINT [PK_orderproduct] PRIMARY KEY NONCLUSTERED
(
orderid,
productid
)

首先修改Order类

     /// <summary>
/// 描述:订单实体,数据库持久化类
/// 创建人:wolfy
/// 创建时间:2014-10-16
/// </summary>
public class Order
{
/// <summary>
/// 订单id
/// </summary>
public virtual Guid OrderID { set; get; }
/// <summary>
/// 下订单时间
/// </summary>
public virtual DateTime OrderDate { set; get; }
/// <summary>
/// 下订单的客户,多对一的关系:orders对应一个客户
/// </summary>
public virtual Customer Customer { set; get; }
/// <summary>
/// 多对多关系,一个订单下可以有多个产品
/// </summary>
public virtual IList<Product> Products { set; get; }
}

修改Order.hbm.xml映射文件如下

 <?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain" namespace="Wolfy.Shop.Domain.Entities">
<class name="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain" table="TB_Order">
<id name="OrderID" column="OrderID" type="Guid" unsaved-value="null">
<generator class="assigned" />
</id>
<property name="OrderDate" column="OrderDate" type="DateTime"
not-null="true" />
<!--多对一关系:Orders属于一个Customer-->
<many-to-one name="Customer" column="CustomerID" not-null="true"
class="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain"
foreign-key="FK_TB_Order_TB_Customer" />
<!--多对多关系:order下有多个product-->
<bag name="Products" generic="true" table="TB_OrderProduct">
<key column="OrderID" foreign-key="FK_TB_OrderProduct_TB_Order"/>
<many-to-many column="ProductID" class="Wolfy.Shop.Domain.Entities.Product,Wolfy.Shop.Domain"
foreign-key="FK_TB_OrderProduct_TB_Product"/> </bag>
</class>
</hibernate-mapping>

修改Product类

     /// <summary>
/// 描述:商品实体,数据库持久化类
/// 创建人:wolfy
/// 创建时间:2014-10-16
/// </summary>
public class Product
{
/// <summary>
/// 商品id
/// </summary>
public virtual Guid ProductID { set; get; }
/// <summary>
/// 商品名称
/// </summary>
public virtual string Name { set; get; }
/// <summary>
/// 商品单价
/// </summary>
public virtual decimal Price { set; get; }
/// <summary>
/// 多对多关系:Product属于多个Orders
/// </summary>
public virtual IList<Order> Orders { get; set; } }

Mappings文件夹中新建Product.hbm.xml映射文件

 <?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain" namespace="Wolfy.Shop.Domain.Entities">
<class name="Wolfy.Shop.Domain.Entities.Product,Wolfy.Shop.Domain" table="TB_Product">
<id name="ProductID" column="ProductID" type="Guid" unsaved-value="null">
<generator class="assigned" />
</id>
<property name="Name" column="Name" type="String"
not-null="true" />
<property name="Price" column="Price" type="float"
not-null="true" />
<!--多对多关系:product属于多个orders-->
<bag name="Orders" generic="true" table="TB_OrderProduct">
<key column="ProductID" foreign-key="FK_TB_OrderProduct_TB_Product"/>
<many-to-many column="OrderID" class="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain"
foreign-key="FK_TB_OrderProduct_TB_Order"/>
</bag>
</class>
</hibernate-mapping>

修改映射文件的属性,这个动作应该养成一个习惯,不管你写的配置文件对不对,只要添加了映射文件,就修改它的属性,如图:

通过上面对比,你会发现多对多关系many-to-many节点的配置相似,只是方向有点区别。

原生SQL查询

查询客户的所有的订单和订单下所有的产品。

CustomerData.cs

         /// <summary>
/// 通过sql查询客户下的订单和产品信息
/// </summary>
/// <param name="cutomerID"></param>
/// <returns></returns>
public IList<Customer> GetCustomerOrderProductsBySQL(Guid cutomerID)
{
//获得ISession实例
ISession session = NHibernateHelper.GetSession();
//使用inner join关联多个表进行查询
return session.CreateSQLQuery("select distinct c.* from TB_Customer c" +
" inner join TB_Order o on o.CustomerID=c.CustomerID" +
" inner join TB_OrderProduct op on o.OrderID=op.OrderID" +
" inner join TB_Product p on op.ProductID=p.ProductID where c.CustomerID=:CustomerID")
//使用AddEntity设置返回的实体。
.AddEntity("Customer", typeof(Customer))
.SetGuid("CustomerID", cutomerID)
.List<Customer>();
}

生成的sql语句

HQL查询

查询客户的所有的订单和订单下所有的产品。

CustomerData.cs

         /// <summary>
/// 通过HQL查询客户下的订单和产品信息
/// </summary>
/// <param name="cutomerID"></param>
/// <returns></returns>
public IList<Customer> GetCustomerOrderProductsByHQL(Guid cutomerID)
{
//获得ISession实例
ISession session = NHibernateHelper.GetSession();
//使用HQL基于面向对象的查询方式
return session.CreateQuery("select distinct c from Customer c inner join c.Orders o inner join o.Products where c.CustomerID=:CustomerID")
.SetGuid("CustomerID", cutomerID)
.List<Customer>();
}

生成的sql语句

通过查看sql及HQL语句可以知道,c.Orders o inner join o.Products 内部为我们查询了所有Order下的所有产品,你会发现我们的查询中并没有出现表TB_OrderProduct,而在sql语句中出现了该表,因为这些信息在映射文件已经描述了。nhibernate通过映射文件,知道如何关联,该关联那张数据表。

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

上面这段代码是@李永京文章中的,这种方式也尝试了c.Orders.elements,总是报以下异常:

NHibernate.Hql.Ast.ANTLR.QuerySyntaxException : c.Orders.elements is not mapped [select distinct c from Customer c , c.Orders.elements o where o.OrderDate > :orderDate]

也许,大概elements在nhibernate 4.0版本中废除了吧,最后没办法,就通过我上面写的那种方式实现了HQL查询,毕竟条条大路通罗马,不能在一条路上吊死吧。

Criteria API关联查询

通过HQL方式,我们已经知道了实体间的关系已经在映射文件中定义好了,所以我们在查询子对象使用子CreateCriteria语句关联对象之间导航,可以很容易地在实体之间指定约束。这里第二个CreateCriteria()返回ICriteria的新实例,并指向Orders实体的元素。第三个指向Products实体的元素。

查询客户的所有的订单和订单下所有的产品。

CustomerData.cs

         /// <summary>
/// 通过HQL查询客户下的订单和产品信息
/// </summary>
/// <param name="cutomerID"></param>
/// <returns></returns>
public IList<Customer> GetCustomerOrderProductsByCriteriaAPI(Guid cutomerID)
{
//获得ISession实例
ISession session = NHibernateHelper.GetSession();
return session.CreateCriteria(typeof(Customer))
.Add(Restrictions.Eq("CustomerID", cutomerID))
.CreateCriteria("Orders")
.CreateCriteria("Products")
.List<Customer>();
}

生成的sql语句

此时查询出来的数据有两条,因为有两个产品,没有对其进行去重。

测试数据如下:

TB_Customer表

TB_Order表

TB_OrderProduct表

TB_Product表

总结

在学习多对多关联查询时,在c.Orders.elements的地方卡在那个地方了,这块还需要在查一查nhibernate版本之间是不是有差异。被一个问题,折腾到现在,本来打算十点半睡觉的(每天最晚十点半上床睡觉),我基本是不熬夜的,有点强迫症,不想带着问题睡觉,很晚了,就写到这儿吧。

参考地址:http://www.cnblogs.com/lyj/archive/2008/10/27/1320764.html#!comments

[NHibernate]多对多关系(关联查询)的更多相关文章

  1. Hibernate,关系映射的多对一单向关联、多对一双向关联、一对一主键关联、一对一外键关联、多对多关系关联

    2018-11-10  22:27:02开始写 下图内容ORM.Hibernate介绍.hibername.cfg.xml结构: 下图内容hibernate映射文件结构介绍 下图内容hibernate ...

  2. Mysql多对多关系的查询

    1.创建user表 2.创建gateway表 3.创建user_gateway表 4.创建device表 5.创建gateway_device表 6.创建一个实体类 public class Devi ...

  3. MyBatis-Plus不写任何resultMap和SQL执行一对一、一对多、多对多关联查询

    对于一对一,一对多的关联查询,Mybatis-Plus官方示例(mybatis-plus-sample-resultmap)在处理时,需要编写查询方法及配置resultMap,并且写SQL. 为了简化 ...

  4. [转]NHibernate之旅(11):探索多对多关系及其关联查询

    本节内容 多对多关系引入 多对多映射关系 多对多关联查询 1.原生SQL关联查询 2.HQL关联查询 3.Criteria API关联查询 结语 多对多关系引入 让我们再次回顾在第二篇中建立的数据模型 ...

  5. NHibernate教程(11)--多对多关联查询

    本节内容 多对多关系引入 多对多映射关系 多对多关联查询 1.原生SQL关联查询 2.HQL关联查询 3.Criteria API关联查询 结语 多对多关系引入 让我们再次回顾在第二篇中建立的数据模型 ...

  6. [NHibernate]一对多关系(关联查询)

    目录 写在前面 文档与系列文章 一对多查询 总结 写在前面 上篇文章介绍了nhibernate的一对多关系如何配置,以及级联删除,级联添加数据的内容.这篇文章我们将学习nhibernate中的一对多关 ...

  7. NHibernate多对多关联映射的实现

    上次用EF演示了数据库多对多关系的操作,这次我们还是引用上次的案例,来演示如何在C#当中使用NHibernate. 首先介绍一下NHibernate框架的来源.熟悉Java编程的读者肯定知道Hiber ...

  8. 用NHibernate处理带属性的多对多关系

    1.引言 老谭在面试开发者的时候,为了考察他们的数据库开发能力,经常祭出我的法宝,就是大学数据库教程中讲到的一个模式:学生选课.这个模式是这种: 在这个模式中,学生(Student)和课程(Cours ...

  9. NHibernate系列文章十九:NHibernate关系之多对多关系(附程序下载)

    摘要 NHibernate的多对多关系映射由many-to-many定义. 从这里下载本文的代码NHibernate Demo 1.修改数据库 添加Product表 添加ProductOrder表 数 ...

随机推荐

  1. ELF Format 笔记(十四)—— 段内容

    ilocker:关注 Android 安全(新手) QQ: 2597294287 一个段 (segment) 由一个或多个节 (section) 组成,但这对 android linker 是透明的, ...

  2. [转]django自定义表单提交

    原文网址:http://www.cnblogs.com/retop/p/4677148.html 注:本人使用的Django1.8.3版本进行测试 除了使用Django内置表单,有时往往我们需要自定义 ...

  3. 控制反转IoC简介

    控制反转IoC简介 在实际的应用开发中,我们需要尽量避免和降低对象间的依赖关系,即降低耦合度.通常的业务对象之间都是互相依赖的,业务对象与业务对象.业务对象与持久层.业务对象与各种资源之间都存在这样或 ...

  4. [WPF系列]-使用Binding来同步不同控件的Dependency property

    简介 项目中经常会用到,同步两个控件的值,本文就简单列举两种方式来同步不同控件的两个Dependency Property. 示例 效果图: 只使用C#代码: //获取slider1的ValueDep ...

  5. Cinder 调试 - 无法挂载到虚拟机

    1.问题 我们有时候在通过云主机挂载云硬盘的时候会出现挂载不上的问题.像这中问题有多种情况导致的. 看一下我遇到的两种情况. 1.权限问题 在cinder节点查看 /var/log/cinder/ci ...

  6. Linux Kernel代码艺术——系统调用宏定义

    我们习惯在SI(Source Insight)中阅读Linux内核,SI会建立符号表数据库,能非常方便地跳转到变量.宏.函数等的定义处.但在处理系统调用的函数时,却会遇到一些麻烦:我们知道系统调用函数 ...

  7. NOIP2012 普及组 T3 摆花——S.B.S.

    题目描述 小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共m盆.通过调查顾客的喜好,小明列出了顾客最喜欢的n种花,从1到n标号.为了在门口展出更多种花,规定第i种花不能超过ai盆,摆花时 ...

  8. js获取键盘按下的键值event.keyCode,event.charCode,event.which的兼容性

    js获取键盘按下的键值有event.keyCode,event.charCode和event.which 其中: 谷歌浏览器对event.keyCode,event.charCode和event.wh ...

  9. oracle 密码有效期

    oracle的密码是存在有效期的,有时候会遇到密码到期需要重设的情况,查看当前密码有效期的语句: SELECT * FROM dba_profiles s WHERE s.profile='DEFAU ...

  10. UVA 11859 Division Game[Nim游戏]

    题意:给定一个N*M的矩阵,每次可以选择同一行中的若干个数,把它们变成它们的质因子.问说先手的可否获胜. 同一行相当于1堆,数量就是所有数的质因子个数之和 #include <iostream& ...