• NHibernate之旅(9):探索父子关系(一对多关系)


    本节内容

    • 引入
    • NHibernate中的集合类型
    • 建立父子关系
    • 父子关联映射
    • 结语

    引入

    通过前几篇文章的介绍,基本上了解了NHibernate,但是在NHibernate中映射关系是NHibernate中的亮点,也是最难掌握的技术。从这篇开始学习这些东西,我将图文结合来说明这里奥秘的知识。

    前几篇,我们的例子只使用了一个简单的Customer对象。但是在客户/订单/产品的经典组合中,他们的关系非常复杂?让我们先回顾在第二篇中建立的数据模型。

    数据关系模型

    在图上,我已经清晰的标注了表之间的关系,首先分析Customer和Order之间的“外键关系”或者称作“父子关系”、“一对多关系”。在分析之前先初步了解NHibernate中的集合。

    NHibernate中的集合类型

    NHibernate支持/定义的几种类型的集合:

    Bag:对象集合,每个元素可以重复。例如{1,2,2,6,0,0},在.Net中相当于IList或者IList<T>实现。

    Set:对象集合,每个元素必须唯一。例如{1,2,5,6},在.Net中相当于ISet或者ISet<T>实现,Iesi.Collections.dll程序集提供ISet集合。

    List:整数索引对象集合,每个元素可以重复。例如{{1,"YJingLee"},{2,"CnBlogs"},{3,"LiYongJing"}},在.Net中相当于ArraryList或者List<T>实现。

    Map:键值对集合。例如{{"YJingLee",5},{"CnBlogs",7},{"LiYongJing",6}},在.Net中相当于HashTable或者IDictionary<Tkey,TValue>实现。

    实际上,我们大多数情况下使用Set集合类型,因为这个类型和关系型数据库模型比较接近。

    建立父子关系

    直接看下面一幅图的两张表:

    外键关系、父子关系、一对多关系

    上面两张表关系表达的意思是:Customer有一个或多个Orders,Orders属于一个Customer。一般而言,我们称Customer为“父”,Order称为“子”。Customer和Order之间关系就有几种说法:“外键关系”、“父子关系”、“一对多关系”都可以。

    1.Customer有一个或多个Orders

    在对象模型中:在Customer类中把Orders作为一个集合,这时可以说Customer对象包含了Orders集合。在.NET中通常这样表述:

    public class Customer
    {
        //......
        public IList<Order> Orders{ get; set; }
    }

    访问对象方式:通过子集合访问:Customer.Orders[...]

    在NHibernate中,通常而言使用Iesi.Collections.dll程序集中的ISet集合,现在修改Customer.cs类,首先需要引用这个程序集,Customer.cs类代码如下:

    using Iesi.Collections.Generic;
    
    namespace DomainModel.Entities
    {
        public class Customer
        {
            public virtual int CustomerId { get; set; }
            public virtual string Firstname { get; set; }
            public virtual string Lastname { get; set; }
            //一对多关系:Customer有一个或多个Orders
            public virtual ISet<Order> Orders { get; set; }
        }
    }

    2.Order属于一个Customer

    在对象模型中:在Order类中把Customer作为单一对象,这时可以说Order对象包含了一个Customer。在.NET中通常这样表述:

    public class Order
    {
        //......
        public Customer Customer{ get; set; }
    }

    其访问对象方式:通过父对象成员访问:Order.Customer

    我们在项目DomainModel层的Entities文件夹中新建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; }
        }
    }

    好了,我们现在完成持久类了,下面看看这两个类如何映射。

    父子关联映射

    在NHibernate中,我们可以通过映射文件来关联对象之间的关系。映射文件定义了:

    • 对象之间关系:一对一、一对多、多对一、多对多关系。
    • 在关系中控制级联行为(Cascade behavior):级联更新、级联删除
    • 父子之间的双向导航(bidirectional navigation)

    1.父实体映射

    父实体(Customer)映射定义了:

    • 集合类型(Bag、Set、List、Map)
    • 在保存、更新、删除操作时的级联行为
    • 关联的控制方向:
      • Inverse="false"(默认):父实体负责维护关联关系
      • Inverse="true":子实体负责维护关联关系
    • 与子实体关联的关系(一对多、多对一、多对多)

    这些具体的设置是NHibernate中的难点所在,以后慢慢讨论这些不同设置下的奥秘之处。

    这一篇初步建立Customer与Order的一对多关系,修改Customer.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.Customer,DomainModel"
             table="Customer">
        <id name="CustomerId" column="CustomerId" type="Int32" 
            unsaved-value="0">
          <generator class ="native"></generator>
        </id>
        <property name="Firstname" column ="Firstname" type="string"
                  length="50" not-null="false"/>
        <property name ="Lastname" column="Lastname" type="string"
                  length="50" not-null="false"/>
        <!--一对多关系:Customer有一个或多个Orders-->
        <set name="Orders" table="`Order`" generic="true" inverse="true">
              <key column="Customer" foreign-key="FK_CustomerOrders"/>
              <one-to-many class="DomainModel.Entities.Order,DomainModel"/>
        </set>
      </class>
    </hibernate-mapping>

    可以看到,在“父”端映射使用Set元素,标明属性名称、表名、子实体负责维护关联关系。

    2.子实体映射

    子实体(Order)映射定义的东西就是父实体少了:与父实体关联的(多对一、一对多、多对多) 关系,并用一个指针来导航到父实体。

    在“子”端通过many-to-one元素定义与“父”端的关联,从“子”端角度看这种关系模型是多对一关联(实际上是对Customer对象的引用)。下面看看many-to-one元素映射属性:

    many-to-one元素定义

    看看这些映射属性具体有什么意义:

    • access(默认property):可选field、property、nosetter、ClassName值。NHibernate访问属性的策略。
    • cascade(可选):指明哪些操作会从父对象级联到关联的对象。可选all、save-update、delete、none值。除none之外其它将使指定的操作延伸到关联的(子)对象。
    • class(默认通过反射得到属性类型):关联类的名字。
    • column(默认属性名):列名。
    • fetch(默认select):可选select和join值,select:用单独的查询抓取关联;join:总是用外连接抓取关联。
    • foreign-key:外键名称,使用SchemaExport工具生成的名称。
    • index:......
    • update,insert(默认true):指定对应的字段是否包含在用于UPDATE或INSERT 的SQL语句中。如果二者都是false,则这是一个纯粹的 “外源性(derived)”关联,它的值是通过映射到同一个(或多个)字段的某些其他特性得到或者通过触发器其他程序得到。
    • lazy:可选false和proxy值。是否延迟,不延迟还是使用代理延迟。
    • name:属性名称propertyName。
    • not-found:可选ignore和exception值。找不到忽略或者抛出异常。
    • not-null:可选true和false值。
    • outer-join:可选auto、true、false值。
    • property-ref(可选):指定关联类的一个属性名称,这个属性会和外键相对应。如果没有指定,会使用对方关联类的主键。这个属性通常在遗留的数据库系统使用,可能有外键指向对方关联表的某个非主键字段(但是应该是一个唯一关键字)的情况下,是非常不好的关系模型。比如说,假设Customer类有唯一的CustomerId,它并不是主键。这一点在NHibernate源码中有了充分的体验。
    • unique:可选true和false值。控制NHibernate通过SchemaExport工具生成DDL的过程。
    • unique-key(可选):使用DDL为外键字段生成一个唯一约束。

    我们来建立“子”端到“父”端的映射,新建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" />
      </class>
    </hibernate-mapping>

    关于如何关联看看上面的属性就一目了然了。

    结语

    这一篇建立Customer和Order之间一对多关联关系,并初步了解NHibernate中的集合。下一篇继续以这一篇为基础,一步一步去挖掘,这是我一贯写法,介绍在NHibernate中使用原生SQL、HQL、Criteria API三种查询方式来关联查询。当然了,从这篇开始,研究的东西就更多了,像查询、加载机制、代理机制、再议并发控制、NHibernate提供的有用类等等。

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

    NHibernate Q&A

    下次继续分享NHibernate!

  • 相关阅读:
    Centos下Oracle11gR2安装教程与自动化配置脚本
    你知道CPU结构也会影响Redis性能吗?
    谈谈InnoDB中的B+树索引
    理论:第十一章:大厂程序员如何使用GitHub快速开发学习
    理论:第十四章:生产环境服务器变慢如何诊断,性能评估
    理论:第十三章:堆溢出,栈溢出的出现场景以及解决方案
    理论:第十二章:Dubbo的运行原理,支持什么协议,与SpringCould相比它为什么效率要高一些,Zookeeper底层原理
    理论:第十章:公平锁,非公平锁,可重入锁,递归锁,自旋锁,读写锁,悲观锁,乐观锁,行锁,表锁,死锁,分布式锁,线程同步锁分别是什么?
    理论:第九章:JVM内存模型,算法,垃圾回收器,调优,四大引用,常见的JVM错误,类加载机制(双亲委派),创建一个对象,这个对象在内存中是怎么分配的?
    理论:第八章:线程是什么,有几种实现方式,它们之间的区别是什么,线程池实现原理,JUC并发包,ThreadLocal与Lock和Synchronize区别
  • 原文地址:https://www.cnblogs.com/aaa6818162/p/2350341.html
Copyright © 2020-2023  润新知