zoukankan      html  css  js  c++  java
  • Fluent NHibernate关系映射

    1.好处:Fluent NHibernate让你不再需要去写NHibernate的标准映射文件(.hbm.xml), 方便了我们的代码重构,提供了代码的易读性,并精简了项目代码

    实现:

    (1)、首先我们通过nuget包管理器添加FluentNHibernate与NHibernate的引用。

    (2)、编写实体类,注意实体的属性都是virtual

    (3)、实体类编写完成以后我们用代码的方式实现对实体的映射

    Fluent NHibernate主要实现的映射关系:一对一、一对多、多对一、多对多

    下面介绍几种映射关系中主讲一对多关联属性

    2. 一对一映射 HasOne<>()

    2.1. 定义支持的关联属性

    Cascade 指该对象在进行操作时关联到的子对象的操作类型

    PropertyRef属性一般用来解决遗留数据库一对多关系的问题

    PropertyRef被关联到此外键的类中的对应属性的名字,若没指定,使用被关联类的主键.

    PropertyRef不是数据库表中的字段名,而是定义的类中的属性名

    Fetch    在外连接抓取或者序列选择抓取选择其一.

    class:被关联的类的名字

    constrained  表明该类对应的表对应的数据库表,和被关联的对象所对应的数据库表之间,通过一个外键引用对主键进行约束。 这个选项影响Save()和Delete()在级联执行时的先后顺序以及决定该关联能否被委托

    2.1.1.映射范例

    用户和用户信息表在实际中是一对一的关系,这两个表之间是通过使用UserID来相互关联的,UserDetail使用的主键ID与User的ID是一致的,所以我们要使用Foregin来获取User的ID,它们有一个共同的ID,在插入Users表的同时也要写入UserDetail表,所以需要添加一对一的限制关系,具体的在User和UsersDetai两表的映射方法如下代码

      public UserMap() 

    { 

             Table("Users"); 

        Id(u => u.UserID).GeneratedBy.Identity() ; 

        Map(u => u.UserName); 

        Map(u => u.Password); 

        Map(u => u.CreateTime); 

        HasOne<UserDetail>(u => u.Detail).Cascade.All().PropertyRef("User"); 

    } 

      public UserDetailMap() 

    { 

    Table("UserDetais"); 

        Id(u => u.UserID).Column("UserID").GeneratedBy.Foreign("User"); 

        HasOne<User>(d => d.User).Cascade.All().Constrained(); 

        Map(u => u.LastUpdated).Nullable(); 

        Component<PersonName>(u => u.Name, p => 

        { 

          p.Map(o => o.FirstName).Column("[First Name]"); 

          p.Map(o => o.LastName).Column("[Last Name]"); 

        }); 

    } 

    2.1.2.补充说明

    上面指定了All说明所有的操作都会关联到子表,还有SaveUpdate在添加和更新时关联子表,另外还有None类型不推荐使用此类型因为会出现很多问题。

    一对一延迟加载

    有时对我们来说,只需要User就可以了,我不需要查询UserDetail,或许你会说,使用以下方式来进行延迟加载:

    HasOne<UserDetail>(u => u.Detail).Cascade.All().LazyLoad();

    虽然Fluent支持,虽然编译通过,但在创建ISessionFactory的时候,却会抛出异常,因为NHibernate不支持HasOne的不支持一对一的延迟Lazy加载的特性, 可以用:

    HasOne<UserDetail>(u => u.Detail).Cascade.All().Fetch.Select();

    HasOne<User>(d => d.User).Cascade.All().Constrained();

    3. 一对多HasMany<>() / 多对一 References<>()

    3.1. 定义支持的关联属性

    KeyColumn  表示主键名

    Cascade表示级联取值,决定是否把对对象的改动反映到数据库中,所以Cascade对所有的关联关系都起作用, Cascade是操作上的连锁反映,Cascade取值可能是以下:

    AllDeleteOrphan       在关联对象失去宿主(解除父子关系)时,自动删除不属于父对象的子对象, 也支持级联删除和级联保存更新.

    DeleteOrphan           删除所有和当前对象解除关联关系的对象        

    All                 级联删除, 级联更新,但解除父子关系时不会自动删除子对象.

    Delete                                  级联删除, 但不具备级联保存和更新

    None                                     所有操作均不进行级联操作

    SaveUpdate                        级联保存(load以后如果子对象发生了更新,也会级联更新). 在执行save/update/saveOrUpdate时进行关联操作,它不会级联删除

    Inverse()    所描述的是对象之间关联关系的维护方式,作用是:是否将对集合对象的修改反映到数据库中。Inverse表示对集合对象的修改会被反映到数据库中;

    为了维持两个实体类(表)的关系,而添加的一些属性,该属性可能在两个实体类(表)或者在一个独立的表里面,这个要看这双方直接的对应关系了: 这里的维护指的是当主控方进行增删改查操作时,会同时对关联关系进行对应的更新,Inverse是操作上的连锁反映。

    ForeignKeyConstraintName   外键约束名

    Access   NHibernate用来访问属性的策略。

    unique: 为外键字段生成一个唯一约束。此外, 这也可以用作PropertyRef的目标属性。这使关联同时具有 一对一的效果。

    OptimisticLock  定这个属性在做更新时是否需要获得乐观锁定(OptimisticLock)。 换句话说,它决定这个属性发生脏数据时版本(version)的值是否增长。

    NotFound   指定外键引用的数据不存在时如何处理: ignore会将数据不存在作为关联到一个空对象(null)处理。

    IsProxy     指定一个类或者接口,在延迟装载时作为代理使用。

    Schema

    3.1.1. 映射范例

    一个用户可以拥有多个订单,一个订单只能拥有一个用户,对于用户来说,不需要每次都加载订单列表,反之订单可能每次都需要加载用户信息。

      public UserMap() 

    { 

             Table("Users"); 

        Id(u => u.UserID).GeneratedBy.Identity() ; 

        Map(u => u.UserName); 

        Map(u => u.Password); 

        Map(u => u.CreateTime); 

        HasOne<UserDetail>(u => u.Detail).Cascade.All().Fetch.Select(); //一对一映射用户和用户信息表

        HasMany<Order>(u => u.Orders).AsSet().KeyColumn("UserID").Cascade.All(); ////处理一对多关系的映射,一个User可以有多个订单, 关联的数据表进行懒加载,主键名为UserID,级联关系所有操作

    }

      public OrderMap() 

      { 

                       Table("Orders"); 

        Id(o => o.OrderID).GeneratedBy.Identity(); 

        Map(o => o.Price); 

        Map(o => o.State).CustomType<OrderState>(); 

        Map(o => o.Address); 

        Map(o => o.Coignee); 

        Map(o => o.CreateTime); 

        Map(o => o.Zip); 

        References<User>(o => o.User).Not.LazyLoad().Column("UserID");  //处理多对一关系,多个Order可以属于一个User

      } 

    /// <summary>

            /// 映射关系实体类的构造函数

            /// 在构造函数中处理好映射关系

            /// </summary>

            public CustomerMapping()

            {

                //指定持久化类对应的数据表

                Table("TB_Customer");

                //自动增长的id

                //Id(i => i.CustomerID);

                //映射关系

                Id<Guid>("CustomerID").GeneratedBy.GuidComb();

                //指定主键后一定要加上主键字段的映射关系,不然返回的id为new Guid(),也就是一串0

                Map(m => m.CustomerID).Nullable();

                Map(m => m.CustomerAddress).Length(50).Nullable();

                Map(m => m.CustomerName).Length(32).Nullable();

                Map(m => m.Version);

                //处理一对多关系的映射,一个客户可以有多个订单

                //关联的数据表进行懒加载,主键名为CustomerID,级联关系所有操作,cascade:All|delete|saveorUpdate,级联删除时需加上Inverse()

                //     Inverse the ownership of this entity. Make the other side of the relationship

                //     responsible for saving.

                HasMany<Order>(h => h.Orders).LazyLoad().AsSet().KeyColumn("CustomerID").Cascade.All().Inverse();

            }

    首先是Tasks表的映射,因为Tasks表是多的一端,所以要添加对Projects表的外键引用关系,另外因为是一种外键引用不关系到父表的操作,所以这里可以使用Cascade.None()。

        public TasksMappping()

        {

            Table("Tasks");

            LazyLoad();

            Id(x => x.ID).Column("TaskID").GeneratedBy.Identity();

            References(x => x.Project).Nullable().Column("ProjectID").Cascade.None();//处理多对一关系,多个Tasks可以属于一个Project

            Map(x => x.Name).Nullable();

        }

    在Projects表中,因为该表中的一个ID会对应多个Tasks所以在添加HasMany方法,来表明Projects和Tasks的多对一的关系,如下代码它会涉及到任务的添加和更新操作,所以需要使用Cascade.SaveUpdate()。

        public ProjectsMapping()

        {

            Table("Projects");

            LazyLoad();

            Id(x => x.ID).Column("ProjectID").GeneratedBy.Identity();

            References(x => x.User).Column("UserID").Cascade.None(); //它的操作不会涉及到Projects的操作

            Map(x => x.Name).Nullable();

            HasMany(x => x.Task).KeyColumn("ProjectID").LazyLoad().Cascade.SaveUpdate();

    //在save或者update Projects的时候会连带着修改Tasks

         }

    3.1.2.补充说明

    一对多的映射,比起一对一来说还相对的简单点,默认是延迟加载,如果项目中,有些地方,需要立即加载,我们也可以使用 FetchMode.Eager 来加载;

    4. 多对多映射 HasManyToMany <>()

    4.1. 定义支持的关联属性

    ParentKeyColumn        定义与A表关联的字段名

    ChildKeyColumn         定义与B表关联的字段名

    Table                  关系表

    4.1.1. 映射范例

    比如电子商务站的订单和产品的关系

      public ProductMap() 

      { 

    Table("Products");

        Id(p => p.ProductID); 

        HasManyToMany<Order>(p => p.Orders) 

          .AsSet() 

          .LazyLoad() 

          .ParentKeyColumn("ProductID") 

          .ChildKeyColumn("OrderID") 

          .Table("OrderProduct"); 

     

        Map(p => p.CreateTime); 

        Map(p => p.Name); 

        Map(p => p.Price); 

      } 

      public OrderMap() 

      { 

    Table("Orders");

        Id(o => o.OrderID).GeneratedBy.Identity(); 

        HasManyToMany<Product>(o => o.Products) 

          .AsSet() 

          .Not.LazyLoad() 

          .Cascade.All() 

          .ParentKeyColumn("OrderID") 

          .ChildKeyColumn("ProductID") 

          .Table("OrderProduct"); 

     

        Map(o => o.Price); 

        Map(o => o.State).CustomType<OrderState>(); 

        Map(o => o.Address); 

        Map(o => o.Coignee); 

        Map(o => o.CreateTime); 

        Map(o => o.Zip); 

        References<User>(o => o.User).Not.LazyLoad().Column("UserID"); //多个订单属于一个客户

      } 

    这里我们用了一个单独的一个表OrderProduct来保存这个多对多关系

    比如 一个Project会有有很多Product,同时一个Product也可能会在多个Project中

        public ProjectsMapping()

        {

            Table("Projects");

            LazyLoad();

            Id(x => x.ID).Column("ProjectID").GeneratedBy.Identity();

            References(x => x.User).Column("UserID").Cascade.None();

            Map(x => x.Name).Nullable();

            HasMany(x => x.Task).KeyColumn("ProjectID").LazyLoad().Cascade.SaveUpdate();

            HasManyToMany(x => x.Product).ParentKeyColumn("ProjectID").ChildKeyColumn("ProductID").Table("ProjectProduct");

        }

        public ProductMapping()

        {

            Table("Product");

            Id(x => x.ID).Column("ProductID").GeneratedBy.Identity();

            Map(x => x.Name).Nullable();

            Map(x => x.Color).Nullable();

            HasManyToMany(x => x.Project).ParentKeyColumn("ProductID").ChildKeyColumn("ProjectID").Table("ProjectProduct");

        }

    4.1.2.补充说明

    具体添加关联的步骤如下:

    (1)在映射的两端同时添加HasManyToMany的关系这样就形成了双向的关联关系

    (2)指定映射的ParentKey和ChildKey,一般会将对象本身的ID指定为ParentKey,关联对象的ID指定为ChildKey

    (3)指定关联关系的关系表,使用Table方法指定关联表,如上示例的Table("ProjectProduct")。

    以上只为突出一对一的双向关联映射,一对多的单向关联和多对多的双向关联关系

  • 相关阅读:
    数据类型装换
    变量及数据类型
    27 网络通信协议 udp tcp
    26 socket简单操作
    26 socket简单操作
    14 内置函数 递归 二分法查找
    15 装饰器 开闭原则 代参装饰器 多个装饰器同一函数应用
    12 生成器和生成器函数以及各种推导式
    13 内置函数 匿名函数 eval,exec,compile
    10 函数进阶 动态传参 作用域和名称空间 函数的嵌套 全局变量
  • 原文地址:https://www.cnblogs.com/shy1766IT/p/4855176.html
Copyright © 2011-2022 走看看