NHibernate将两个表连接成一个带有复合键的实体

时间:2011-07-12 11:26:51

标签: nhibernate fluent-nhibernate nhibernate-mapping fluent-nhibernate-mapping

我有以下数据结构:

+---------+
|Resume   |
+---------+
|Id (PK)  |
|IsActive |
|...      |
|..       |
|.        |
+---------+

+--------------------+
|Resume_Translation  |
+--------------------+
|ResumeId (PK, FK)   |
|Language (PK)       |
|Title               |
|Description         |
|...                 |
|..                  |
|.                   |
+--------------------+

所以我可以拥有两个连接表的数据:

+----------------------------------------------------------+
|Id | IsActive | ResumeId | Language | Title | Description |
+----------------------------------------------------------+
|1  | true     | 1        | 'fr'     | 'One' | 'One desc'  |
|1  | true     | 1        | 'pl'     | 'Raz' | 'Raz Opis'  |
|2  | true     | 2        | 'fr'     | 'B'   | 'bla bla'   |
|3  | true     | 3        | 'fr'     | 'C'   | 'C bla bla' |
+----------------------------------------------------------+

从我的域名来看,我只关心Resume实体。我不希望Resume实体的集合为Resume_Translations,因为我只有一个Resume实体具有当前翻译。

public class Resume
{
    public virtual int Id{ get; protected internal set; }

    public virtual string Language { get; protected internal set; }

    public virtual string Title { get; protected internal set; }

    public virtual string Description { get; protected internal set; }

    public virtual bool IsActive { get; protected internal set; }
}

我目前与Fluent NHibernate的映射如下:

public class ResumeMap : ClassMap<Resume>
{

    public ResumeMap()
    {
        Table("Resume");
        Id(x => x.Id);
        Map(x => x.IsActive);
        // other properties
        Join("Resume_Translation", m =>
                        {
                            m.Fetch.Join();
                            m.Map(x => x.Language).Length(5);
                            m.Map(x => x.Title).Length(100);
                            m.Map(x => x.Description).Length(200);
                        });
    }
}

我可以从存储库中获得我想要的东西,而不仅仅是传递WHERE谓词的简历和我想要的语言。

但是我在插入和更新值方面遇到了一些问题。

我的问题是:如何定义NHibernate仅在Resume_Translation表中插入新记录而不是更新当前实体的记录的映射?

所以我想要实现的是,如果我在数据库中有以下记录:

|2  | true     | 2        | 'fr'     | 'B'   | 'bla bla'   |

加入对于表之间的一对一关系很有用,所以如果我把它放到我的实体中并且我改变了语言和翻译,那么nhibernate正在执行更新,我可以理解它。如果我尝试通过不同的语言和翻译添加具有相同Id的新实体,则nhibernate会产生一个错误,即密钥已经存在并且我也理解它。

所以,当然,我走的是错误的道路,但如果有人能够指出我如何能够实现我想要的映射的正确解决方案,我将非常感激。

另一个问题是,您如何从业务角度处理实体及其翻译?

先谢谢你的帮助。

托马斯

3 个答案:

答案 0 :(得分:2)

似乎与我有一对多的关系。我个人会在简历对象中收集 ResumeTranslation 对象。然后,我会将其作为标准对象进行映射。

然后,您可以将另一个属性 ActiveResumeTranslation 添加到代表您current translation简历实体。

答案 1 :(得分:2)

使用字典作为密钥使用字典怎么样?

public class ResumeTranslation
{
    public virtual string Title { get; protected internal set; }

    public virtual string Description { get; protected internal set; }
}

public class Resume
{
    public virtual int Id{ get; protected internal set; }

    // language is the key to the translation
    // you may even want to hide the dictionary from the public interface of
    // this class and only provide access to a "current" language.
    public virtual IDictionary<string, ResumeTranslation> Translations { get; private set; }

    public virtual bool IsActive { get; protected internal set; }
}

并相应地将其映射为具有复合元素的map(对不起,我没有流利使用,所以不要问我它会是什么样子)。它与您的数据库模型完全匹配。

答案 2 :(得分:2)

Stefan走在正确的轨道上。我已经调整了他的建议,使其具有双向关联,这将使更新变得更容易。这种方法的一个缺点是,您需要在插入时手动分配ResumeTranslation实例的Resume属性,以便NHibernate将Resume表键正确分配给ResumeTranslation行。因此,考虑到您正在映射的关联,这就是它在Fluent NH中的外观:

public class ResumeTranslation
{
    public virtual string Title { get; protected internal set; }

    public virtual string Description { get; protected internal set; }

             //Needed for bi-directional association:
    public virtual Resume Resume { get; set; }
}


public class ResumeTranslationMap : ClassMap<ResumeTranslation>
{

    public ResumeTranslationMap()
    {
        Table("ResumeTranslation");
        CompositeId()
            .KeyReference(kp => kp.Resume, "ResumeId")
            .KeyProperty(kp => kp.Language, "Language");

        Map(x => x.Title);
        Map(x => x.Description);
    }
}

public class ResumeMap : ClassMap<Resume>
{

    public ResumeMap()
    {
        Table("Resume");
        Id(x => x.Id);
        Map(x => x.IsActive);
        // other properties

        HasMany(c => c.Translations)
            .Inverse()
            .KeyColumn("id") //May not be required but here for reference
            .Cascade.All();
    }
}