0

我有以下映射:

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

现在,我真的不想在域中为角色创建一个单独的类,我只需要角色名称。但是,在 DB Roles 中仍应规范化为单独的 table Role (Id, Name)

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

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

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

4

4 回答 4

2

你实际上不会得到你希望的答案,仅仅因为这是不可能的。(N)Hibernate 是一个 Object-Relational-Mapping 框架,支持三种映射策略:

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

formula它还允许您通过使用or等​​来偏离这一点sql-insert,但是正如您所发现的,这些最终只会给您带来更多痛苦,Hibernate 社区不鼓励您,并且不利于您的代码的可维护性。

解决方案?

实际上,这很简单。您不想为Role. 我假设您的意思是您不想公开Role 类型的类,并且您不想一直键入prObject.Role.Name。Just 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 classes,它使您能够以任何您喜欢的方式“装饰”您的类。优点:如果您使用生成的表映射,您可以根据需要多次重新生成。当然,部分类应该放在单独的文件中,因此考虑到您希望减少“膨胀”,这可能不是一个好的场景。

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

虽然可以直接使用 NHibernate 执行此操作,但 q. 在我有时间看之前已经关闭(没有人错,我只是没有时间)。如果我有时间通过​​ Hibernate HBM 映射进行更新,我会更新,即使我不同意这种方法。当最终结果对其他程序员来说不太清楚且整体不太清楚时,与 Hib 的高级概念搏斗是不好的(该表去了哪里?为什么该表没有 IDao 抽象?另请参阅NHibernate 最佳实践S#arp)。然而,这个练习还是很有趣的。

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

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

于 2009-11-03T01:55:30.093 回答
1

我个人会创建一个像 Yassir 这样的角色类

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

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

但是,我认为采用这种方法意味着您将无法更新您的角色,因为它正在重新定义视图。

编辑:要更新您可以添加<sql-insert>,<sql-update> and <sql-delete>到映射文件中的角色,以便 cascade-all 可以工作

于 2009-10-29T05:30:14.410 回答
1

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

于 2009-10-28T13:25:25.307 回答
0

这是整个 OO 纯粹主义事物的最大转折点。当然,目标是拥有一个工作应用程序。不是完美类层次结构的某个版本。那么,如果您必须编写“prObject.Role.Name”而不是“prObject.Role”怎么办。这如何帮助您制作更好更可靠的程序?

从应用程序设计纯粹主义者的角度来看,您想要的完全是错误的。一个人可以有多个角色,一个角色通常可以分配给几个人。当无延迟数据模型是每个人有多个角色时,为什么还要费尽心思来强制执行一个不切实际的每个人一个角色的类层次结构?

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

于 2009-11-04T04:08:12.087 回答