当前位置: 首页 > 编程日记 > 正文

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

本节内容

  • 多对多关系引入
  • 多对多映射关系
  • 多对多关联查询
    • 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属于一个Customerpublic virtual Customer Customer { get; set; }//多对多关系:Order有多个Productspublic 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属于多个Orderspublic 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知识!
  • 请到NHibernate中文社区下载本系列相关源码。

下次继续分享NHibernate!

转载于:https://www.cnblogs.com/lyj/archive/2008/10/27/1320764.html

相关文章:

自动化运维—saltstack

2019独角兽企业重金招聘Python工程师标准>>> 自动化运维——saltstack 、ansible 一、自动化运维介绍 传统运维&#xff1a;传统运维效率低&#xff0c;大多工作需要人工完成&#xff0c;工作繁琐&#xff0c;容易出错&#xff0c;每日重复做相同的事情&#xff0c;…

史上最浅显易懂的Git教程!

Git初学者很好的一篇教程 mark : ) http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 转载于:https://www.cnblogs.com/anthony0859/p/3900327.html

狎昵关系和依恋情结辨诠

Inappropriate Intimacy&#xff08;狎昵关系&#xff09; 表现&#xff1a;两个classes过于亲密&#xff0c;花费太多时间去探究彼此的private成分 解决&#xff1a;你可以采用 Move Method 和 Move Field 帮它们划清界线&#xff0c;从而减少狎昵行径。你也可以看看是否运用…

win2000.win2003关闭端口详解--防黑必备

我相信有很多人都不知道自己开了什么端口.更加不知道怎么关闭端口. 你可以用查看端口的软件查看. 也可以通过在运行里输入"cmd" 在弹出的cmd命令行里输入 netstat -an 来查看自己开放端口.ip地址的后面的就是端口号. 以下是我自己写的一篇关于关闭端口的详细步骤和多…

网站基于vs,复选框,单选款

前端代码&#xff1a; <% Page Language"C#" AutoEventWireup"true" CodeFile"Default2.aspx.cs" Inherits"Default2" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.or…

thinphp 整合ueditor

我的ueditor是部署在public/editor 部署前台页面 <script type"text/javascript" > var UEDITOR_HOME_URL: "__PUBLIC__/ueditor/" </script><script id"container" name"$des" type"text/plain">这里写你…

笔画宽度变化(C++和matlab算法)

最近一直在看工作方面的书籍&#xff0c;把论文的事情搁置了&#xff0c;之前承诺的贴代码的事一直拖。现在把代码整理发上来&#xff0c;只有核心部分的&#xff0c;都不是我写的&#xff0c;我是网上整理下载的&#xff0c;matlab代码的效果比较差。 全部文件网盘下载地址:ht…

bzoj1227: [SDOI2009]虔诚的墓主人(树状数组,组合数)

传送门 首先&#xff0c;对于每一块墓地&#xff0c;如果上下左右各有$a,b,c,d$棵树&#xff0c;那么总的虔诚度就是$C_k^a*C_k^b*C_k^c*C_k^d$ 那么我们先把所有的点都给离散&#xff0c;然后按$x$为第一关键字&#xff0c;$y$为第二关键字&#xff0c;那么同一横坐标的一定在…

[导入]源代码版本控制(一)

开发过程当中源代码的版本控制一直是个大问题。项目规模小了还好办&#xff0c;人的脑子还能记过来&#xff0c;项目大了&#xff0c;可能用各式各样的表格来记录版本信息和源代码内容&#xff0c;但这个办法本身的文档组织又是个问题&#xff0c;谁来维护&#xff1f;谁来更改…

重构技巧分别能够解决哪些代码味道

1.提炼类可以解决的5种代码味道&#xff1a; 过大类 重复代码 基本类型偏执 令人迷惑的暂时值域 狎昵关系 2.将类内联化可以解决的3种代码味道 冗赘类 夸夸其谈的未来性 霰弹式修改 3.隐藏委托关系解决的2种代码味道 狎昵关系 过度耦合的消息链 4.复制被监视的数据 过大类 5.以…

python爬取电影和美食数据实战

本文使用的是requests正则来匹配网页内容&#xff0c;对于数据量较多的采用了多线程抓取的方法&#xff0c;共3个案例&#xff0c;分别是抓取猫眼电影TOP100榜单和淘票票正在热映的电影信息、以及美团的美食数据。这几个案例采用的方法大同小异。1、首先选择想要爬取的网站2、确…

Asp.Net页面执行流程分析

在我的上一篇文章中说到了HttpModule、HttpHandle的简单使用&#xff0c;我们可以利用它们在页面请求的过程中加入自己的事件处理程序。那么在一个aspx页面请求时后台到底做了什么&#xff1f;当然asp.net做了很多事情&#xff0c;过程也比较复杂&#xff0c;本文主要分析一下大…

正则验证非法字符

function regText(text){var reg /^[\s\u4e00-\u9fa5a-z0-9_-]{0,}$/;if(!reg.exec(text)){console.log("非法字符")}else{console.log("有效字符")} } regText("abc") 验证 &#xff1a;汉字、英文、数字、下划线、中划线、空格 转载于:https…

活动排序工具之双代号网络(AOA)与单代号网络(AON)[cont.]

箭线图ADM/双代号网络AOA 图示 箭线表示活动 节点表示一个活动的开始或结束 三要素&#xff1a;结点、箭线、线路 唯一使用虚活动的活动排序工具&#xff0c;虚活动用虚线箭头表示&#xff0c;没有历时&#xff0c;不需资源&#xff0c;只表达活动关系的需要 只使用一种活动之…

并发任务的可视化

一、任务要求&#xff1a;在linux系统中设计一个父进程&#xff0c;三个子进程(A,B,C)。子进程A,B同时被父进程启动来计算&#xff08;不实现具体的计算任务&#xff0c;先用CPU空跑来代替&#xff09;。进程A计算5分钟&#xff0c;而进程B计算8分钟。当进程A,B都计算完成后才能…

银监会警示担保圈贷款风险 联保贷款变异 防多米诺效应

互保联保本是解决小微企业以及农村金融贷款需求的重要创新&#xff0c;但却在部分行业、部分地区逐渐变异&#xff0c;成为引发风险事件的诱因。 据媒体报道&#xff0c;银监会近日发文要求加强企业担保圈贷款风险的防范和化解工作。银监会警示&#xff0c;担保圈企业风险较高的…

SharePoint 2007 系列(4) -Site Settings

Site administration 转载于:https://www.cnblogs.com/xuxiaoguang/archive/2008/11/05/1326913.html

软件项目管理重点总结

文章目录概论走进项目管理把控环境&#xff0c;控制过程整合项目资源控制项目范围保障项目进度驾驭项目成本保证项目质量协调项目人力资源改善项目的沟通应对项目风险关注项目的采购和外包概论 项目的定义&#xff1a;为创造一个特定的产品、服务或者成果而采取的临时性的努力…

jQuery发送含有数组参数的ajax请求以及后台Struts2的OGNL解析错误

当使用jquery1.3以上版本时&#xff0c;进行ajax参数传值时&#xff0c;会出现以下的一个错误: ognl.ExpressionSyntaxException: Malformed OGNL expression: f[] [ognl.ParseException: Encountered " "]" "] "" at line 1, column 3. 这个错…

数据绑定以及Container.DataItem绑定技巧

数据绑定以及Container.DataItem绑定技巧 灵活的运用数据绑定操作绑定到简单属性:<%#UserName%>绑定到集合:<asp:ListBox id"ListBox1" datasource<%# myArray%> runat"server">绑定到表达式:<%#(class1.property1.ToString() "…

LeetCode 76. Minimum Window Substring / 567. Permutation in String

76. Minimum Window Substring 典型Sliding Window的问题&#xff0c;维护一个区间&#xff0c;当区间满足要求则进行比较选择较小的字串&#xff0c;重新修改start位置。 思路虽然不难&#xff0c;但是如何判断当前区间是否包含所有t中的字符是一个难点&#xff08;t中字符有重…

计算机网络中的协议数据单元的控制信息主要包括哪些内容

在计算机网络的数据传输过程中会对数据进行封装&#xff0c;俗称加头(链路层还会加尾)&#xff0c;增加的控制信息主要包括以下内容&#xff1a; 地址(Address)&#xff1a;用来标识发送端或接收端差错检测编码(Error-detecting code)&#xff1a;用于差错检测或纠正协议控制(…

jQuery 超屏加载

jQuery 超屏加载&#xff0c;当文档超出屏幕的高度时&#xff0c;加载最新下个列数据 $(window).scroll(function () {var height $(document).height(); //页面的高度var keheight $(window).height(); //浏览器可视的高度var sheight $(document).scrollTop(); //滚动的高…

自己动手,打造轻量级VSCode/C#环境代替LinqPad

.Net 的项目都挺重的&#xff0c;一直想找一个轻量级的 CSharp 环境&#xff0c;能像Python那样&#xff0c;选一个文件就能跑的。之前用的是 LinqPad&#xff0c;但它的缺点也很明显&#xff1a; &#xff08;1&#xff09; 不付费&#xff0c;自动完成不能用&#xff08;…

让html:error只显示第一条错误信息

struts-config.xml 中的 <plug-in className"org.apache.struts.validator.ValidatorPlugIn"> 里面加上 <set-property property"stopOnFirstError" value"true"/> 就可以了 </plug-in> 转载于:https://www.cnblogs.com/kakak…

(C++)除基取余法:将十进制数转化为Q进制数

所谓基&#xff0c;就是指将要转换成的进制Q。 除基取余的意思就是&#xff1a;每次将待转换数除以Q&#xff0c;然后将得到的余数作为低位存储&#xff0c;而商则继续除以Q并重复上面的操作&#xff0c;直至商0时&#xff0c;将所有位从高到低输出就可以得到Q进制数。 代码实…

《C++ Primer 4th》读书笔记 第5章-表达式

原创文章&#xff0c;转载请注明出处&#xff1a; http://www.cnblogs.com/DayByDay/p/3912114.html 转载于:https://www.cnblogs.com/DayByDay/p/3912114.html

vue - check-versions.js for child_process

webpack之类的配置文件. webpack.base.conf.js

EasyPHP-2.0b1+ Mantis-1.1.0安装及技巧

转载&#xff1a; EasyPHP-2.0b1 Mantis-1.1.0安装及技巧 注&#xff1a;部分配置来源网络&#xff0c;写此文仅为以后配置提供参考 Mantis是一个轻量级的brower的bug管理系统&#xff0c;界面直观&#xff0c;简单易用&#xff0c;安装简单&#xff0c;支持多语言&#xff0c;…