目录

写在前面

文档与系列文章

多对多关系关联查询

总结

写在前面

上篇文章介绍了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. jQuery的deferred对象详解

    jQuery的deferred对象详解请猛击下面的链接 http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_ ...

  2. Neutron 理解(14):Neutron ML2 + Linux bridge + VxLAN 组网

    学习 Neutron 系列文章: (1)Neutron 所实现的虚拟化网络 (2)Neutron OpenvSwitch + VLAN 虚拟网络 (3)Neutron OpenvSwitch + GR ...

  3. 修改VMware虚拟机里面的显卡名称

    修改VMware虚拟机里面的显卡名称 现在有些蛋疼坑爹的游戏,会检测显卡的名称,如果其中有“Vmware”这类的字眼,就会提示程序在虚拟机中运行而拒绝执行(太不科学了嘛,我要是把我的实机驱动名称也改成 ...

  4. php中header函数参数的Cache-control:private,no-cache,must-revalidate,max-age是使用方法!

    由于我做的是登录,也就是用户登录每次登陆时都要来访问服务器,不需要在客户机做缓存,于是在网上找了找,发下一下文章不错于是传了上来.网页的缓存是由HTTP消息头中的“Cache-control”来控制的 ...

  5. TestFlight

    链接: 如何使用TestFlight进行Beta测试 [转]TestFlight APP测试(IOS如何让上架前给其他人测试) 转]TestFlight APP测试(IOS如何让上架前给其他人测试) ...

  6. nodejs express 静态文件的路径

    当express 设置为静态文件服务器的时候.可以通过2种方式进行方位: 1,通过设置app.use('路径1','../a/b/image') express 路径的形式,如 src="路 ...

  7. 十分钟轻松让你认识ASP.NET 5(MVC6)

    ASP.NET 5差不多快发布了.自己也学习了有两个月了.今天给没有接触asp.net 5的同学写一个简单地十分钟教程,教你认识一下asp.net 5. 1.安装kvm 首先,你需要以管理员权限打开c ...

  8. Caliburn.Micro学习笔记(四)----IHandle<T>实现多语言功能

    Caliburn.Micro学习笔记目录 说一下IHandle<T>实现多语言功能 因为Caliburn.Micro是基于MvvM的UI与codebehind分离, binding可以是双 ...

  9. C#进阶系列——DDD领域驱动设计初探(七):Web层的搭建

    前言:好久没更新博客了,每天被该死的业务缠身,今天正好一个模块完成了,继续来完善我们的代码.之前的六篇完成了领域层.应用层.以及基础结构层的部分代码,这篇打算搭建下UI层的代码. DDD领域驱动设计初 ...

  10. 响应式疑惑? CSS单位研究

    各种单位要搞清楚,自己试一试,实践出真知! 2.屏幕分辨率    响应式 哦,电脑的分辨率:1440x900表示水平有1440个像素点哦! 垂直有900个像素点. 而网页在浏览器中,所以宽度是电脑的分 ...