NHibernate:将单列从多对一映射到基本类型

时间:2009-10-24 11:15:33

标签: nhibernate nhibernate-mapping many-to-one

我有以下映射:

<set name="People" lazy="true" table="ProjectPeople">
  <key column="ProjectId" />
  <composite-element class="PersonRole">
    <many-to-one name="Person" column="PersonId" cascade="save-update" not-null="true" />
    <many-to-one name="Role" column="RoleId" cascade="save-update" not-null="true"  />
  </composite-element>
</set>

现在,我真的不想在域中有一个单独的Role类,我只需要Role名称。但是,在DB Roles中,仍应将其标准化为单独的表Role (Id, Name)

如何映射它以便人们使用以下PersonRole类?

public class PersonRole {
    public virtual Person Person { get; set; }
    public virtual string Role { get; set; }
}

更新:添加赏金,似乎不仅对我有用的问题。

4 个答案:

答案 0 :(得分:2)

你实际上不会得到你希望得到的答案,因为它是不可能的。 (N)Hibernate是一个对象关系映射框架,支持three kinds of mapping策略:

  • 每个类层次结构的表
  • 每个子类的表
  • 每个具体类的表

它还允许你通过使用formulasql-insert等来偏离这一点,但正如你所发现的,这些只会让你最终感到痛苦,不受Hibernate的鼓励社区,并且不利于代码的可维护性。

解决方案?

实际上,它非常简单。您不希望使用Role的类。我假设您的意思是您不希望公开类型为Role的类,并且您不希望始终键入prObject.Role.Name。只需prObject.Role,它应该返回一个字符串。您有几种选择:

  1. 在PersonRole中使用内部类,此类可以是内部的也可以是私有的。添加属性设置和更新成员字段的角色;
  2. 使用内部课程。添加属性设置和更新成员字段的角色;
  3. 我们来看看选项2:

    // mapped to table Role, will not be visible to users of your DAL
    // class can't be private, it's on namespace level, it can when it's an inner class
    internal class Role 
    {
        // typical mapping, need not be internal/protected when class is internal
        // cannot be private, because then virtual is not possible
        internal virtual int Id { get; private set; }
        internal virtual string Name { get; set; }
    }
    
    // the composite element
    public class PersonRole
    {
        // mapped properties public
        public virtual Person Person { get; set; }
    
        // mapped properties hidden
        internal virtual Role dbRole { get; set; }
    
        // not mapped, but convenience property in your DAL
        // for clarity, it is actually better to rename to something like RoleName
        public string Role     /* need not be virtual, but can be */
        { 
            get
            {
                return this.dbRole.Name;
            }
            set
            {
                this.dbRole.Name = value;    /* this works and triggers the cascade */
            }
        }
    }
    

    映射可以看起来像预期的那样。结果:您没有违反每个一个表的规则(编辑:提问者说他​​明确要违反该规则,而Hib支持它,这是正确的),但是你已经通过使用典型的面向对象技术隐藏对象的修改和访问。所有NH功能(级联等)仍然按预期工作。

    (N)Hibernate完全是关于这类决策的:如何在不牺牲清晰度,简洁性或可维护性或违反OO或ORM规则的情况下,为数据库创建一个经过深思熟虑且安全的抽象层。


    更新(q。关闭后)

    在处理此类问题时我经常使用的其他优秀方法是:

    • 正常创建映射(即,每个表一个类,我知道你不喜欢它,但它是最好的)并使用扩展方法:

       // trivial general example
       public static string GetFullName(this Person p)
       {
           return String.Format("{0} {1}", p.FirstName, p.LastName);
       }
      
       // gettor / settor for role.name
       public static string GetRoleName(this PersonRole pr)
       {
           return pr.Role == null ? "" : pr.Role.Name;
       }
       public static SetRoleName(this PersonRole pr, string name)
       {
           pr.Role = (pr.Role ?? new Role());
           pr.Role.Name = name;
       }
      
    • 正常创建映射但使用partial class es,这使您可以按照自己喜欢的方式“装饰”您的类。优点:如果您使用生成的表映射,则可以根据需要重新生成。当然,部分类应该放在单独的文件中,所以考虑到你希望减少“膨胀”,这可能目前不是一个好的方案。

       public partial class PersonRole
       {
           public string Role {...}
       }
      
    • 也许最简单:只为ToString()重载Role,这使其适合在String.Format和朋友中使用,但当然不会使其可分配。默认情况下,无论如何,每个实体类或POCO都应该有ToString()重载。

    虽然可以直接用NHibernate来做这个,但是q。在我有时间看之前已经关闭了(没有错,我只是没有时间)。如果我通过Hibernate HBM映射找到时间,我会更新,即使我不同意这种方法。当最终结果对其他程序员不太清楚而且总体上不太清楚时,与Hib的高级概念搏斗是不好的(那个表去了哪里?为什么那个表没有IDao抽象?参见{{3} }和NHibernate Best Practices)。然而,这项练习很有意思。

    考虑对“最佳实践”的评论:在典型情况下,它不应该只是“每个表一个类”,而是每个表一个IDaoXXX,一个DaoConcreteXXX和一个GetDaoXXX,您使用类/接口层次结构区分只读和读/写表。每个表至少有四个类/行代码。这通常是自动生成的,但为数据层(dal)提供了非常清晰的访问层(dao)。数据层最好保持尽可能简洁。这些“最佳做法”中没有任何内容阻止您使用扩展方法或部分方法将Role.Name移至Role

    这些是最好的一般做法。在某些特殊或典型的情况下,并不总是可行或可行,甚至不必要。

答案 1 :(得分:1)

我不认为有可能将多对一映射到原始类型,如果我是你,我会将一个Role类添加到模型中

答案 2 :(得分:1)

我个人会创建一个像Yassir

这样的Role类

但是如果你想使用你目前拥有的结构,那么创建一个视图,其中包含你的人员表和角色描述的foriegn Key。

修改Set mapping table以指向新视图 然后修改您的角色映射,使其成为属性而不是多对一映射。

然而,采用这种方法,我认为这意味着您将无法更新您的角色,因为它正在重新呈现视图。

修改:要更新角色,您可以将<sql-insert>,<sql-update> and <sql-delete>添加到地图文件中,以便级联全部可以使用

答案 3 :(得分:0)

这是整个OO纯粹主义事物的最大转折点。 当然,目标是拥有一个有效的应用程序。不是一个完美的类层次结构的somebodies版本。那么如果你必须编码“prObject.Role.Name”而不是“prObject.Role”呢?这有助于您制定更可靠的计划吗?

从应用程序设计纯粹主义的角度来看,你想要的只是一个简单的错误。一个人可以有多个角色,通常可以将角色分配给多个人。 为什么要解决所有这些麻烦,以便在每个人的课程层次结构中强制执行一个不切实际的角色,而且每个人都有多个角色?

如果您确实拥有“每人只有一个角色”规则,那么它应该在基础数据模型中反映出来。