我有以下映射:
<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; }
}
更新:添加赏金,似乎不仅对我有用的问题。
答案 0 :(得分:2)
你实际上不会得到你希望得到的答案,因为它是不可能的。 (N)Hibernate是一个对象关系映射框架,支持three kinds of mapping策略:
它还允许你通过使用formula
或sql-insert
等来偏离这一点,但正如你所发现的,这些只会让你最终感到痛苦,不受Hibernate的鼓励社区,并且不利于代码的可维护性。
实际上,它非常简单。您不希望使用Role
的类。我假设您的意思是您不希望公开类型为Role的类,并且您不希望始终键入prObject.Role.Name
。只需prObject.Role
,它应该返回一个字符串。您有几种选择:
我们来看看选项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规则的情况下,为数据库创建一个经过深思熟虑且安全的抽象层。
在处理此类问题时我经常使用的其他优秀方法是:
正常创建映射(即,每个表一个类,我知道你不喜欢它,但它是最好的)并使用扩展方法:
// 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”呢?这有助于您制定更可靠的计划吗?
从应用程序设计纯粹主义的角度来看,你想要的只是一个简单的错误。一个人可以有多个角色,通常可以将角色分配给多个人。 为什么要解决所有这些麻烦,以便在每个人的课程层次结构中强制执行一个不切实际的角色,而且每个人都有多个角色?
如果您确实拥有“每人只有一个角色”规则,那么它应该在基础数据模型中反映出来。