本节内容

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

多对多关系引入

让我们再次回顾在第二篇中建立的数据模型:

在图上,我已经清晰的标注了表之间的关系,上两篇分析Customer和Order之间的“外键关系”或者称作“父子关系”、“一对多关系”和关联查询,这一篇以Order为中心,分析Order和Product之间的关系,直接看下面一幅图的两张表:

上面两张表关系表达的意思是:Order有多个Products,Product属于多个Orders。我们称Order和Product是多对多关系,这一篇我们来深入讨论在NHibernate如何映射多对多关系及其多对多关联查询。

多对多映射关系

1.Order有多个Products

有了上两篇的基础,现在直奔主题,建立关系。大家可以把这篇的代码作为模板,以后在工作中参考。

修改Order.cs类代码如下:

namespace DomainModel.Entities
{
public class Order
{
public virtual int OrderId { get; set; }
public virtual DateTime OrderDate { get; set; }
//多对一关系:Orders属于一个Customer
public virtual Customer Customer { get; set; }
//多对多关系:Order有多个Products
public virtual IList<Product> Products { get; set; }
}
}

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

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="DomainModel" namespace="DomainModel"> <class name="DomainModel.Entities.Order,DomainModel" table="`Order`" > <id name="OrderId" column="OrderId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<property name="OrderDate" column="OrderDate" type="DateTime" not-null="true" />
<!--多对一关系:Orders属于一个Customer-->
<many-to-one name="Customer" column="Customer" not-null="true"
class="DomainModel.Entities.Customer,DomainModel"
foreign-key="FK_CustomerOrders" />
<!--多对多关系:Order有多个Products-->
<bag name="Products" generic="true" table="OrderProduct">
<key column="`Order`" foreign-key="FK_OrderProducts"/>
<many-to-many column="Product"
class ="DomainModel.Entities.Product,DomainModel"
foreign-key="FK_ProductOrders"/>
</bag
>
</class>
</hibernate-mapping>

在多对多关系中,其两方都使用Bag集合和many-to-many元素。看看上面各个属性和one-to-many,many-to-one属性差不多。

2.Product属于多个Orders

在项目DomainModel层的Entities文件夹中新建Product.cs类,编写代码如下:

namespace DomainModel.Entities
{
public class Product
{
public virtual int ProductId { get; set; }
public virtual string Name { get; set; }
public virtual float Cost { get; set; }
//多对多关系:Product属于多个Orders
public virtual IList<Order> Orders { get; set; }
}
}

在项目DomainModel层的Mappings文件夹中新建Product.hbm.xml映射文件,编写代码如下:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="DomainModel" namespace="DomainModel">
<class name="DomainModel.Entities.Product,DomainModel" table="Product"> <id name="ProductId" column ="ProductId" type="Int32" unsaved-value="0">
<generator class="native"/>
</id>
<property name="Name" column="Name" type="string" not-null="true" length="50"/>
<property name="Cost" column="Cost" type="float" not-null="true"/>
<!--多对多关系:Product属于多个Orders-->
<bag name="Orders" generic="true" table="OrderProduct">
<key column="Product" foreign-key="FK_ProductOrders"/>
<many-to-many column="`Order`"
class="DomainModel.Entities.Order,DomainModel"
foreign-key="FK_OrderProducts"/>
</bag
>
</class>
</hibernate-mapping>

多对多关联查询

使用NHibernate中提供的三种查询方法实现多对多关联查询,查询返回所有订单和产品的顾客列表。

1.原生SQL关联查询

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

这里需要使用Join告诉查询如何在表之间关联。无论多么复杂的关系,我们必须在查询语句中指定返回值。这里使用AddEntity设置返回的实体。

2.HQL关联查询

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>();
}

因为在映射文件已经定义实体之间一对多、多对多关系,NHibernate通过映射文件知道如何去关联这些实体,我们不需要在查询语句中重复定义。这里使用查询和上一篇使用HQL关联查询语句一样,NHibernate完全可以去关联对象,实现查询订单和产品。

3.Criteria API关联查询

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

public IList<Customer> UseCriteriaAPI_GetCustomerswithOrdersHavingProduct()
{
return _session.CreateCriteria(typeof(Customer))
.Add(Restrictions.Eq("Firstname","YJing"))
.CreateCriteria("Orders")
.Add(Restrictions.Gt("OrderDate",new DateTime(2008,10,1)))
.CreateCriteria("Products")
.Add(Restrictions.Eq("Name","Cnblogs"))
.List<Customer>();
}

下面我用一幅图简单明了的说明这段代码神秘之处,也显示了一些约束条件。

编写一个测试用例测试UseCriteriaAPI_GetCustomerswithOrdersHavingProduct()方法,遍历列表,看看产品名称是否为“Cnblogs”,OK!测试通过。

[Test]
public void UseCriteriaAPI_GetCustomerswithOrdersHavingProductTest()
{
IList<Customer> customers = _relation.UseCriteriaAPI_GetCustomerswithOrdersHavingProduct();
bool found = false;
foreach (Customer c in customers)
{
foreach (Order o in c.Orders)
{
foreach (Product p in o.Products)
{
if (p.Name == "Cnblogs")
{
found = true;
break;
}
} }
}
Assert.IsTrue(found);
}

下面再写个简单例子查询产品Id所关联的一些顾客,测试用例同上面差不多,自己修改下就可以啦。

public IList<Customer> UseCriteriaAPI_GetCustomerswithOrdersHavingProduct(int productId)
{
return _session.CreateCriteria(typeof(Customer))
.CreateCriteria("Orders")
.CreateCriteria("Products")
.Add(Restrictions.Eq("ProductId", productId))
.List<Customer>();
}

结语

这一篇通过全盘代码的形式完成NHibernate中的多对多关系映射,使用NHibernate中提供的三种查询方法实现了多对多关联查询。希望对你有所帮助,多多练习。我们下次继续讨论NHibernate话题,像延迟加载、立即加载、对象状态等话题,关于朋友回复说讨论更多话题,我只能说,再等等吧,慢慢来,这才第十一篇,先把基础的问题弄清楚。

本系列链接:NHibernate之旅系列文章导航

NHibernate Q&A

下次继续分享NHibernate!

NHibernate教程(11)--多对多关联查询的更多相关文章

  1. mybatis实战教程二:多对一关联查询(一对多)

    多对一关联查询 一.数据库关系.article表和user表示多对一的关系 CREATE TABLE `article` ( `id` ) NOT NULL AUTO_INCREMENT, `user ...

  2. mybatis 14: 多对一关联查询

    业务背景 根据订单id查询订单的信息,以及该订单所属的客户的基本信息(不包括该客户自己的订单信息) 两张数据表 客户表 订单表 实体类 客户实体类:Customer private Integer i ...

  3. Python--day64--找到作者关联的所有书籍对象、ORM多对多关联查询的原理

    找到当前作者关联的所有书籍对象: ORM多对多关联查询的原理: 编辑作者:

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

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

  5. mybatis多对多关联查询

    多对多关系 一个学生可以选多门课程,而一门课程可以由多个学生选择,这就是一个典型的多对多关联关系.所谓多对多关系,其实是由两个互反的一对多关系组成.即多对多关系都会通过一个中间表来建立,例如选课表.学 ...

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

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

  7. mybatis实现多表一对一,一对多,多对多关联查询

    原文:https://blog.csdn.net/m0_37787069/article/details/79247321 1.一对一关键字:association作用:针对pojo对象属性的映射  ...

  8. mybatis 一对多和多对一关联查询

    首先  数据库量表之间字段关系(没有主外键) studentmajor表的id字段对应student表里major字段 两个实体类 package com.model; import java.uti ...

  9. mybatis多对多关联查询——(十)

    1.需求 查询用户及用户购买商品信息. 2     sql语句 查询主表是:用户表 关联表:由于用户和商品没有直接关联,通过订单和订单明细进行关联,所以关联表: orders.orderdetail. ...

随机推荐

  1. 压缩[SCOI2007]

    题目描述 给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息.压缩后的字符串除了小写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前 ...

  2. Python Socket 简单聊天室2

    上篇文章写了一个简单的单线程的一问一答的简单聊天室.这次我们使用SocketServer模块搭建一个多线程异步的聊天室. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ...

  3. 案例:Oracle报错ASM磁盘组不存在或没有mount

    案例:Oracle报错ASM磁盘组不存在或没有mount 环境:RHEL 6.5 + Oracle Standby RAC 11.2.0.4 我做Standby RAC实验时,在恢复控制文件时,报错无 ...

  4. UITableView移除某行的分割线和让分割线宽度为cell的宽度

    1.移除 UITableView 某一行的分割线 所谓移除,其实就是使其偏移出 cell 的显示范围,看不到即移除. 方法1: 移除系统的分割线,自己定义每行的分割线 self.tableView.s ...

  5. Mysql事务处理详细讲解及完整实例下载

    一.Mysql事务概念 MySQL 事务主要用于处理操作量大,复杂度高的数据.由一步或几步数据库操作序列组成逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行.在 MySQL 中只有使用了 Inn ...

  6. oracle 错误码 ORA-00119 / ORA-00130

    今天启动oracle时居然报错,错误信息如下: SQL> startup ORA-00119: invalid specification for system parameter LOCAL_ ...

  7. HTML中直接写js 函数

    1.在HTML中直接写JS函数: <body onload="javascript:{window.location.href='http://www.baidu.com/'}&quo ...

  8. Python 接口:从协议到抽象基类

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Helvetica } 抽象基类的常见用途:实现接口时作为超类使用.然后,说明抽象基类如何检查 ...

  9. JSP向后台传递参数的四种方式

    Jsp页面传值的方法 一.通过Form表单提交传值 客户端通过Form表单提交到服务器端,服务器端通过 Java代码 request.getParameter(String xx); 来取得参数(xx ...

  10. oracle运行速度与效率高的秘密

    使用过Oracle的人都知道,Oracle的运行速度与效率,在同类数据库中是名列前茅的,特别是对大量数据进行访问时,更加有出色的表现.那么,Oracle数据库是靠什么实现的呢?笔者下面将通过一系列的文 ...