休眠状态:将单列从多对一映射到原始类型
-
06-07-2019 - |
题
我有以下映射:
<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 (Id, Name)
.
如何映射它以便人们使用以下 PersonRole 类?
public class PersonRole {
public virtual Person Person { get; set; }
public virtual string Role { get; set; }
}
更新: :添加赏金,似乎这个问题不仅对我有用。
解决方案
我个人会创建一个像Yassir
这样的Role类但是如果你想使用你目前拥有的结构,那么创建一个视图,其中包含你的人员表和角色描述的foriegn Key。
修改Set mapping table以指向新视图 然后修改您的角色映射,使其成为属性而不是多对一映射。
然而,采用这种方法,我认为这意味着您将无法更新您的角色,因为它正在重新呈现视图。
编辑:要更新角色,您可以将<sql-insert>,<sql-update> and <sql-delete>
添加到地图文件中,以便级联全部可以正常工作
其他提示
你实际上不会得到你希望的答案,因为这是不可能的。(N)Hibernate 是一个对象关系映射框架和支持 三种映射 策略:
- 每个类层次结构的表
- 每个子类的表
- 每个具体类别的表
它还允许您通过使用来偏离这一点 formula
或者 sql-insert
等等,但正如您所发现的,这些最终只会给您带来更多痛苦,并且不受 Hibernate 社区的鼓励,并且不利于代码的可维护性。
解决方案?
其实很简单。你不想使用一个类 Role
. 。我假设你的意思是你不想 暴露 Role 类型的类,并且您不想键入该类 prObject.Role.Name
每时每刻。只是 prObject.Role
, ,它应该返回一个字符串。您有多种选择:
- 在 PersonRole 中使用内部类,该类可以是内部的或私有的。添加一个属性Role,用于设置和更新成员字段;
- 使用内部类。添加一个属性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 规则的情况下,为数据库创建一个经过深思熟虑且安全的抽象层。
更新(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 映射来完成此操作,我会进行更新,即使我不同意这种方法。当最终结果对于其他程序员来说不太清楚并且整体不太清楚时(该表去了哪里?为什么该表没有 IDao 抽象?也可以看看 NHibernate 最佳实践 和 S#arp)。然而,这个练习仍然很有趣。
考虑到对“最佳实践”的评论:在典型情况下,它不应该只是“每个表一个类”,还应该为每个表提供一个 IDaoXXX、一个 DaoConcreteXXX 和一个 GetDaoXXX,其中使用类/接口层次结构来区分只读表和读/写表。每个表至少有四个类/行代码。这通常是自动生成的,但为数据层 (dal) 提供了非常清晰的访问层 (dao)。数据层最好尽可能保持简洁。这些“最佳实践”不会阻止您使用扩展方法或部分方法进行移动 Role.Name
进入 Role
.
这些是最好的 一般的 做法。在某些特殊或典型情况下,这并不总是可能或可行,甚至是必要的。
我不认为有可能将多对一映射到原始类型,如果我是你,我会将一个Role类添加到模型中
这是整个OO纯粹主义事物的最大转折点。 当然,目标是拥有一个有效的应用程序。不是一个完美的类层次结构的somebodies版本。那么如果你必须编码<!> quot; prObject.Role.Name <!>而不是<!> quot; prObject.Role <!> quot;。这有助于您制定更可靠的计划吗?
从应用程序设计纯粹主义的角度来看,你想要的只是一个简单的错误。一个人可以有多个角色,通常可以将角色分配给多个人。 为什么要解决所有这些麻烦,以便在每个人的课程层次结构中强制执行一个不切实际的角色,而且每个人都有多个角色?
如果你真的有一个<!>,那么每个人只有一个角色<!>然后它应该在基础数据模型中反映出来。