正确映射NHibernate的多态关系

时间:2010-02-18 18:34:27

标签: nhibernate nhibernate-mapping polymorphism

我正在尝试使用NHibernate 2.0.1创建一个每层次表映射。 我有一个基类,其中包含其他类继承的每个子类的属性。所有这些对象都持久保存到一个名为Messages的表中,该表包含每个类的所有可能字段。有一个SourceID是鉴别器,应该指出每个子类返回哪个Poco。这是我目前的映射。

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="NS.Core"
                   namespace="NS.Core.Model">
  <class name="BaseMessage" table="Messages">
    <id name="MessageID" type="Int64">
      <column name="MessageID" />
      <generator class="native" />
    </id>
    <discriminator column="SourceID" type="Int32"/>
    <property name="DateCreated" access="property" column="DateCreated" type="DateTime" not-null="true"/>
    <property name="DatePublished" access="property" column="DatePublished" type="DateTime"/>
    <property name="SourceID" access="property" column="SourceID" type="Int32"/>
    <many-to-one name="User" column="UserID" access="property" cascade="none" lazy="false" fetch="join" outer-join="true" />
    <subclass name="NMessage" discriminator-value="0">
      <property name="Body" access="property" column="Body" type="String"/>
    </subclass>
    <subclass name="BMessage" discriminator-value="1">
      <property name="Title"  access="property" column="Title" type="String"/>
      <property name="Body" access="property" column="Body" type="String"/>
    </subclass>
    <subclass name="CMessage" discriminator-value="2">
      <property name="Url"  access="property" column="Url" type="String"/>
      <property name="Body" access="property" column="Body" type="String"/>
    </subclass>
  </class>
</hibernate-mapping>

我收到错误查询说无法将鉴别器值格式化为实体NS.Core.Model.BaseMessage的SQL字符串,所以我在此类上放置了一个鉴别器值,但它应该永远不会返回基类。这导致了一些antlr错误。

我对这个问题采取了错误的方法吗?我想查询该表并获取所有从基类继承的不同POCO的列表。它永远不会返回基类本身。

下面的

是BaseMessage.cs

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;

namespace NS.Core.Model
{

    [Serializable]
    [DataContract]
    [KnownType(typeof(User))]
    public class BaseMessage
    {
        #region Private variables
        private long _messageID;
        private DateTime? _dateCreated;
        private DateTime? _datePublished;
        private int _sourceID;
        private User _user = new User();

        #endregion

        #region Properties
        [DataMember]
        public virtual long MessageID
        {
            get { return _messageID; }
            set { this._messageID = value; }
        }
          [DataMember]
        public virtual DateTime? DateCreated
        {
            get { return _dateCreated; }
            set { this._dateCreated = value; }
        }
        [DataMember]
        public virtual DateTime? DatePublished
        {
            get { return _datePublished; }
            set { this._datePublished = value; }
        }
        [DataMember]
        public virtual int SourceID
        {
            get { return _sourceID; }
            set { this._sourceID = value; }
        }
        [DataMember]
        public virtual User User
        {
            get
            {
                if (this._user != null)
                { return this._user; }
                else { return new User(); }
            }
            set { this._user = value; }
        }
        #endregion
    }
}

1 个答案:

答案 0 :(得分:9)

方法很健全。我已经好好完成了几次。

您可以在代码中明确指出我的BaseMessage的默认构造函数是否受到保护。

您还需要在基类上声明一个鉴别符值。

我更喜欢鉴别器的字符串值,因为在执行SQL查询或报告时更清楚。此外,由于BaseMessage的实例不存在,因此将使用null作为其鉴别器值。

<class name="BaseMessage" table="Messages" discriminator-value="null">
    <id />
    <discriminator column="SourceID" />
    <subclass name="NMessage" discriminator-value="NMessage">
    </subclass>
    <subclass name="BMessage" discriminator-value="BMessage">
    </subclass>
    <subclass name="CMessage" discriminator-value="CMessage">
    </subclass>
</class>

另外,我看到您已将属性映射到discriminator列。您应该使用一个方法返回类的唯一内容 - 在本例中是代码。

请注意,保存后无法更改映射实体的类。甚至不改变鉴别器。如果您确实通过SQL进行了更改,那么您的二级缓存仍将保留原始类的版本。

class BaseMessage
{
    public virtual string MessageType { return null; }
}

class NMessage : BaseMessage
{
    public override string MessageType { return "NMessage"; }
}

最后,您的映射文件过于冗长,因为它包含默认值。可以删除以下属性和元素:

  • access =“property” - 这是默认
  • type =“String” - 您可以从.NET类
  • 推断出您使用的所有类型
  • column =“COL” - 默认值与名称
  • 相同
  • 类似于id列元素

所有Message子类都具有属性Body,因此将其移动到基类映射。如果此字段可能比数据库varchar长,则它应该是一个文本列,并且type =“StringCLob”,它映射到.NET中的字符串

<class name="BaseMessage" table="Messages" discriminator-value="null">
    <id name="MessageID">
        <generator class="native" />
    </id>
    <discriminator column="SourceID"/>
    <property name="DateCreated" />
    <property name="DatePublished" />
    <many-to-one name="User" column="UserID" cascade="none" lazy="false" fetch="join" outer-join="true" />
    <property name="Body" type="StringCLob" />
    <subclass name="NMessage" discriminator-value="NMessage">
    </subclass>
    <subclass name="BMessage" discriminator-value="BMessage">
        <property name="Title" />
    </subclass>
    <subclass name="CMessage" discriminator-value="CMessage">
        <property name="Url" />
    </subclass>
</class>