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

目录

写在前面

文档与系列文章

多对多关系关联查询

总结

写在前面

上篇文章介绍了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语句如下:

1 ALTER TABLE tb_orderproduct WITH NOCHECK ADD
2     CONSTRAINT [PK_orderproduct] PRIMARY KEY  NONCLUSTERED
3      (
4           orderid,
5           productid
6      )

首先修改Order类

 1     /// <summary>
 2     /// 描述:订单实体,数据库持久化类
 3     /// 创建人:wolfy
 4     /// 创建时间:2014-10-16
 5     /// </summary>
 6     public class Order
 7     {
 8         /// <summary>
 9         /// 订单id
10         /// </summary>
11         public virtual Guid OrderID { set; get; }
12         /// <summary>
13         /// 下订单时间
14         /// </summary>
15         public virtual DateTime OrderDate { set; get; }
16         /// <summary>
17         /// 下订单的客户,多对一的关系:orders对应一个客户
18         /// </summary>
19         public virtual Customer Customer { set; get; }
20         /// <summary>
21         /// 多对多关系,一个订单下可以有多个产品
22         /// </summary>
23         public virtual IList<Product> Products { set; get; }
24     }

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

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain"  namespace="Wolfy.Shop.Domain.Entities">
 3   <class name="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain" table="TB_Order">
 4     <id name="OrderID" column="OrderID" type="Guid" unsaved-value="null">
 5       <generator class="assigned" />
 6     </id>
 7     <property name="OrderDate" column="OrderDate" type="DateTime"
 8               not-null="true" />
 9     <!--多对一关系:Orders属于一个Customer-->
10     <many-to-one  name="Customer" column="CustomerID" not-null="true"
11                  class="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain"
12                  foreign-key="FK_TB_Order_TB_Customer" />
13     <!--多对多关系:order下有多个product-->
14     <bag name="Products" generic="true" table="TB_OrderProduct">
15       <key column="OrderID" foreign-key="FK_TB_OrderProduct_TB_Order"/>
16       <many-to-many  column="ProductID" class="Wolfy.Shop.Domain.Entities.Product,Wolfy.Shop.Domain"
17      foreign-key="FK_TB_OrderProduct_TB_Product"/>
18
19     </bag>
20   </class>
21 </hibernate-mapping>

修改Product类

 1     /// <summary>
 2     /// 描述:商品实体,数据库持久化类
 3     /// 创建人:wolfy
 4     /// 创建时间:2014-10-16
 5     /// </summary>
 6     public class Product
 7     {
 8         /// <summary>
 9         /// 商品id
10         /// </summary>
11         public virtual Guid ProductID { set; get; }
12         /// <summary>
13         /// 商品名称
14         /// </summary>
15         public virtual string Name { set; get; }
16         /// <summary>
17         /// 商品单价
18         /// </summary>
19         public virtual decimal Price { set; get; }
20         /// <summary>
21         /// 多对多关系:Product属于多个Orders
22         /// </summary>
23         public virtual IList<Order> Orders { get; set; }
24
25     }

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

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

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

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

原生SQL查询

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

CustomerData.cs

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

生成的sql语句

HQL查询

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

CustomerData.cs

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

生成的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

 1         /// <summary>
 2         /// 通过HQL查询客户下的订单和产品信息
 3         /// </summary>
 4         /// <param name="cutomerID"></param>
 5         /// <returns></returns>
 6         public IList<Customer> GetCustomerOrderProductsByCriteriaAPI(Guid cutomerID)
 7         {
 8             //获得ISession实例
 9             ISession session = NHibernateHelper.GetSession();
10             return session.CreateCriteria(typeof(Customer))
11                 .Add(Restrictions.Eq("CustomerID", cutomerID))
12                 .CreateCriteria("Orders")
13                 .CreateCriteria("Products")
14                 .List<Customer>();
15         }

生成的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

时间: 2024-12-21 17:08:55

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

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

2018-11-10  22:27:02开始写 下图内容ORM.Hibernate介绍.hibername.cfg.xml结构: 下图内容hibernate映射文件结构介绍 下图内容hibernate映射文件中主键自增规则.Hibernate实例状态(瞬时状态.持久化状态.托管状态).Hibernate初始化类获取session等方法 下图内容保存数据过程 下面内容保存数据顺序.查询数据方法 get().load()和延迟加载.删除数据 下图内容删除对象顺序.修改数据顺序 下面内容关联关系映射.

mybatis 一对一,一对多,多对多关系映射查询操作

定义两个类(对应数据库内两张表) User ,Account,每个Account属于一个User User类 及其 对应的IUserDao package com.itheima.domain; import java.io.Serializable; import java.util.Date; import java.util.List; public class User implements Serializable { private Integer id; private Strin

NHibernate之旅(11):探索多对多关系及其关联查询

本节内容 多对多关系引入 多对多映射关系 多对多关联查询 1.原生SQL关联查询 2.HQL关联查询 3.Criteria API关联查询 结语 多对多关系引入 让我们再次回顾在第二篇中建立的数据模型: 在图上,我已经清晰的标注了表之间的关系,上两篇分析Customer和Order之间的"外键关系"或者称作"父子关系"."一对多关系"和关联查询,这一篇以Order为中心,分析Order和Product之间的关系,直接看下面一幅图的两张表: 上面两

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

本节内容 多对多关系引入 多对多映射关系 多对多关联查询 1.原生SQL关联查询 2.HQL关联查询 3.Criteria API关联查询 结语 多对多关系引入 让我们再次回顾在第二篇中建立的数据模型: 在图上,我已经清晰的标注了表之间的关系,上两篇分析Customer和Order之间的“外键关系”或者称作“父子关系”.“一对多关系”和关联查询,这一篇以Order为中心,分析Order和Product之间的关系,直接看下面一幅图的两张表: 上面两张表关系表达的意思是:Order有多个Produc

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

本节内容 多对多关系引入 多对多映射关系 多对多关联查询 1.原生SQL关联查询 2.HQL关联查询 3.Criteria API关联查询 结语 多对多关系引入 让我们再次回顾在第二篇中建立的数据模型: 在图上,我已经清晰的标注了表之间的关系,上两篇分析Customer和Order之间的"外键关系"或者称作"父子关系"."一对多关系"和关联查询,这一篇以Order为中心,分析Order和Product之间的关系,直接看下面一幅图的两张表: 上面两

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

目录 写在前面 文档与系列文章 一对多查询 总结 写在前面 上篇文章介绍了nhibernate的一对多关系如何配置,以及级联删除,级联添加数据的内容.这篇文章我们将学习nhibernate中的一对多关系的关联查询.前面文章中也介绍的nhibernate的查询:HQL,条件查询,原生SQL查询. 文档与系列文章 [Nhibernate]体系结构 [NHibernate]ISessionFactory配置 [NHibernate]持久化类(Persistent Classes) [NHibernat

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

1.引言 老谭在面试开发人员的时候,为了考察他们的数据库开发能力,常常祭出我的法宝,就是大学数据库教程中讲到的一个模式:学生选课.这个模式是这样的: 在这个模式中,学生(Student)和课程(Course)都是实体,分别有主键Id.考试成绩(Score)是学生和课程之间的多对多关系. 基于这个模式,对于新手,可以出一些简单查询要求,对于熟手,可以出一些复杂的查询要求,用起来得心应手. 但今天要说的重点是,怎么用NHibernate实现这个模式.和一般多对多关系稍有不同的是,这个关系带有一个属性

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

本节内容 关联查询引入 一对多关联查询 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).这一节分别使用这三种方式来

mybatis关联查询问题(一对多、多对一)

mybatis 提供了高级的关联查询功能,可以很方便地将数据库获取的结果集映射到定义的Java Bean 中.下面通过一个实例,来展示一下Mybatis对于常见的一对多和多对一关系复杂映射是怎样处理的. 设计一个简单的博客系统,一个用户可以开多个博客,在博客中可以发表文章,允许发表评论,可以为文章加标签.博客系统主要有以下几张表构成: Author表:作者信息表,记录作者的信息,用户名和密码,邮箱等. Blog表   :  博客表,一个作者可以开多个博客,即Author和Blog的关系是一对多.