代码之家  ›  专栏  ›  技术社区  ›  Andrey Shchekin

nhibernate:将单个列从多个映射到一个基元类型

  •  0
  • Andrey Shchekin  · 技术社区  · 15 年前

    我有以下映射:

    <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>
    

    现在,我真的不想为域中的角色有一个单独的类,我只需要角色名。但是,in-db角色仍应规范化为单独的表。 Role (Id, Name) .

    我如何映射它,以便人们使用下面的PersonRole类?

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

    更新 :加上赏金,似乎是一个不仅对我有用的问题。

    4 回复  |  直到 15 年前
        1
  •  1
  •   Nathan Fisher    15 年前

    就我个人而言,我会创建一个像Yassir这样的角色班。

    但是,如果您想使用当前的结构,那么创建一个视图,其中包含人员表的外键和角色描述。

    修改集合映射表以指向新视图 然后修改角色映射,使其成为属性而不是多对一映射。

    然而,采用这种方法,我认为意味着你将无法更新你的角色,因为它正在重新定义一个视图。

    编辑: 更新您可以添加的角色 <sql-insert>,<sql-update> and <sql-delete> 到您的映射文件,以便级联全部工作

        2
  •  2
  •   Abel    15 年前

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

    • 每个类层次结构的表
    • 每个子类一张表
    • 每个混凝土等级的表

    它还允许您使用 formula sql-insert 等等,但是正如您所发现的,这些只会最终导致您更加痛苦,不受休眠社区的鼓励,并且对代码的可维护性有害。

    解决方案?

    其实很简单。您不想将类用于 Role . 我想你是说你不想 暴露 一个类型为Role的类,您不需要键入 prObject.Role.Name 总是。只是 prObject.Role ,它将返回一个字符串。您有几个选项:

    1. 使用一个内部类,例如,personrole,这个类可以是内部的或私有的。添加设置和更新成员字段的属性角色;
    2. 使用内部类。添加设置和更新成员字段的属性角色;

    让我们检查一下选项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 */
            }
        }
    }
    

    映射可以如预期的那样。结果:您没有违反每个类一个表规则( 编辑:asker说他明确想违反这个规则,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 它能让你随意“装饰”你的班级。优点:如果您使用表的生成映射,您可以根据自己的需要随时重新生成。当然,部分类应该放在单独的文件中,因此考虑到您希望减少“膨胀”,目前这可能不是一个好的场景。

       public partial class PersonRole
       {
           public string Role {...}
       }
      
    • 也许最简单:只是超载 ToString() 对于 角色 使其适用于 String.Format 和朋友,但当然不会让它成为可分配的。默认情况下,每个实体类或POCO都应具有 ToString()。 无论如何,过载。

    虽然可以直接用NHibernate来做这个,但在我有时间看之前Q.已经关闭了(没有人出错,我只是没有时间)。如果我有时间通过HibernateHBM映射进行更新,即使我不同意这种方法。当最终结果对其他程序员来说不太清楚,总体上也不太清楚时,与hib的高级概念作斗争是不好的(那张表去哪儿了?为什么该表没有IDAO抽象?也见 NHibernate Best Practices S#arp )不过,这个练习还是很有趣的。

    考虑到对“最佳实践”的评论:在典型情况下,它不仅应该是“每个表一个类”,而且应该是每个表一个IDAOXXX、一个DAOC具体XXX和一个GetDAOXXX,在这些表中,您可以使用类/接口层次结构来区分只读表和读/写表。每个表至少有四个类/行代码。这通常是自动生成的,但为数据层(DAL)提供了非常清晰的访问层(DAO)。数据层尽可能简单。这些“最佳实践”都不能阻止您使用扩展方法或部分方法来移动 Role.Name 进入之内 角色 .

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

        3
  •  1
  •   Hannoun Yassir    15 年前

    如果我是你,我不可能把多对一映射到一个原始类型,我会在模型中添加一个角色类。

        4
  •  0
  •   James Anderson    15 年前

    这是整个OO清教徒的最大转折点。 当然,目标是有一个有效的应用程序。不是完美的类层次结构的somebody版本。所以,如果您必须对“probject.role.name”而不是“probject.role”进行编码,该怎么办?这有助于你制定更可靠的计划吗?

    从应用程序设计纯粹主义的角度来看,你想要的只是一个明显的错误。一个人可以有几个角色,一个角色通常可以分配给几个人。 当取消定义的数据模型为每人多个角色时,为什么要花这么多的时间来强制一个不切实际的每人一个角色的类层次结构呢?

    如果您真的有一个“每个人只有一个角色”规则,那么它应该在底层数据模型中被反射。

    推荐文章