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")。

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

  • 相关阅读:
    .Net开发中IIS启动后,网站不能访问。0x80004005 无法执行程序。所执行的命令为 .....问题
    .Net开发中项目无法引用/项目引用失败/引用文件出现黄色警告图标,并生成失败的问题
    Mac系统安装Docker
    小程序开发----微信小程序实现下拉刷新功能
    小程序开发----微信小程序navigate跳转没反应
    小程序开发----小程序点击复制文本内容到剪贴板
    小程序开发----微信小程序直接写&nbsp;设置空格无效
    python字符串的反转
    python2项目迁移到python3
    robotframework废弃的关键词以及替代的关键词(关键词找不到,可能已经过期了)
  • 原文地址:https://www.cnblogs.com/shy1766IT/p/4855176.html
Copyright © 2011-2022 走看看