[NHibernate]多对多关系(关联查询)
目录
写在前面
上篇文章介绍了nhibernate中对一对多关系进行关联查询的几种方式,以及在使用过程需要注意的问题。这篇文章对多对多关系的查询处理也采用上篇文章的描述方式进行说明。
文档与系列文章
[NHibernate]持久化类(Persistent Classes)
[NHibernate]集合类(Collections)映射
[NHibernate]缓存(NHibernate.Caches)
[NHibernate]NHibernate.Tool.hbm2net
[NHibernate]Nhibernate如何映射sqlserver中image字段
[NHibernate]条件查询Criteria Query
多对多关系关联查询
多对多关系非常常见,比如系统中常用的权限管理问题,一个用户有多个权限,当然一个权限可以属于多个用户(这样描述只是为了对多对多关系有个感性的认识)。那么在咱们的客户/订单/产品,这三张表中有没有多对多的关系呢?
订单可以有多个产品,一种产品可以属于多个订单(这个地方有点绕,如果再有一个数量的字段,比较好理解,下一个订单,只是库存减少了,但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]多对多关系(关联查询)的更多相关文章
- Hibernate,关系映射的多对一单向关联、多对一双向关联、一对一主键关联、一对一外键关联、多对多关系关联
2018-11-10 22:27:02开始写 下图内容ORM.Hibernate介绍.hibername.cfg.xml结构: 下图内容hibernate映射文件结构介绍 下图内容hibernate ...
- Mysql多对多关系的查询
1.创建user表 2.创建gateway表 3.创建user_gateway表 4.创建device表 5.创建gateway_device表 6.创建一个实体类 public class Devi ...
- MyBatis-Plus不写任何resultMap和SQL执行一对一、一对多、多对多关联查询
对于一对一,一对多的关联查询,Mybatis-Plus官方示例(mybatis-plus-sample-resultmap)在处理时,需要编写查询方法及配置resultMap,并且写SQL. 为了简化 ...
- [转]NHibernate之旅(11):探索多对多关系及其关联查询
本节内容 多对多关系引入 多对多映射关系 多对多关联查询 1.原生SQL关联查询 2.HQL关联查询 3.Criteria API关联查询 结语 多对多关系引入 让我们再次回顾在第二篇中建立的数据模型 ...
- NHibernate教程(11)--多对多关联查询
本节内容 多对多关系引入 多对多映射关系 多对多关联查询 1.原生SQL关联查询 2.HQL关联查询 3.Criteria API关联查询 结语 多对多关系引入 让我们再次回顾在第二篇中建立的数据模型 ...
- [NHibernate]一对多关系(关联查询)
目录 写在前面 文档与系列文章 一对多查询 总结 写在前面 上篇文章介绍了nhibernate的一对多关系如何配置,以及级联删除,级联添加数据的内容.这篇文章我们将学习nhibernate中的一对多关 ...
- NHibernate多对多关联映射的实现
上次用EF演示了数据库多对多关系的操作,这次我们还是引用上次的案例,来演示如何在C#当中使用NHibernate. 首先介绍一下NHibernate框架的来源.熟悉Java编程的读者肯定知道Hiber ...
- 用NHibernate处理带属性的多对多关系
1.引言 老谭在面试开发者的时候,为了考察他们的数据库开发能力,经常祭出我的法宝,就是大学数据库教程中讲到的一个模式:学生选课.这个模式是这种: 在这个模式中,学生(Student)和课程(Cours ...
- NHibernate系列文章十九:NHibernate关系之多对多关系(附程序下载)
摘要 NHibernate的多对多关系映射由many-to-many定义. 从这里下载本文的代码NHibernate Demo 1.修改数据库 添加Product表 添加ProductOrder表 数 ...
随机推荐
- linux命令之tail
tail用于输出文件末尾部分.一个比较有用的功能是tail + grep实现类似于安卓开发时调试使用的logcat,具体操作是: 一般我是用SecureCRT连接linux,然后使用SecureCRT ...
- Linux吃掉我的内存
在Windows下资源管理器查看内存使用的情况,如果使用率达到80%以上,再运行大程序就能感觉到系统不流畅了,因为在内存紧缺的情况下使用交换分区,频繁地从磁盘上换入换出页会极大地影响系统的性能.而当我 ...
- c++适配器
容器适配器是是标准库中通用的概念,包括容器适配器.迭代器适配器和函数适配器,本质上,适配器是使一种事物的行为类似于另一种事物的的行为的一种机制,容器适配器使一种已经存在的容器类型采用另一种不同的抽象类 ...
- UVA11090 Going in Cycle!! [spfa负环]
https://vjudge.net/problem/UVA-11090 平均权值最小的回路 为后面的做个铺垫 二分最小值,每条边权减去他,有负环说明有的回路平均权值小于他 spfa求负环的时候可以先 ...
- Vijos P1196吃糖果游戏[组合游戏]
描述 Matrix67和Shadow正在做一个小游戏. 桌子上放着两堆糖果,Matrix67和Shadow轮流对这些糖果进行操作.在每一次操作中,操作者需要吃掉其中一堆糖果,并且把另一堆糖果分成两堆( ...
- centos 7 安装音乐播放器(亲测可用)(转载)
http://www.cnblogs.com/boyiliushui/p/4530625.html
- ajax获取数据的形象比喻,助于理解记忆
过程 创建对象(打开浏览器) 连接服务器(输入网址) 发送请求(按下回车) 服务器接收并返回数据(显示对应的网址网站内容) 原理
- mysqli_fetch_assoc php的新的库函数
注释:mysql_fetch_assoc() 函数 定义和用法mysql_fetch_assoc() 函数从结果集中取得一行作为关联数组. 返回根据从结果集取得的行生成的关联数组,如果没有更多行,则返 ...
- LeetCode "419. Battleships in a Board"
The follow-up question is fun: "Could you do it in one-pass, using only O(1) extra memory and w ...
- iOS下的按钮css去除原生样式
IOS环境下的按钮都是经过美化的,但通常我们在设计web app的时候不需要这些看上去老土的样式,所以,去除这些显得很有必要. 下面这句代码就是重置这些样式的: input[type=button]{ ...