NHibernate 使用复合键将两个表连接成一个实体

发布于 2024-11-19 15:21:24 字数 2652 浏览 1 评论 0原文

我有以下数据结构:

+---------+
|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 谓词、Resume 的 Id 和我想要的语言即可。

但是,我在插入和更新值时遇到一些问题。

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

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

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

Join 对于表之间的一对一关系很有用,因此如果我将其放入我的实体中并更改语言和翻译,nhibernate 正在执行更新,我可以理解它。如果我尝试通过不同的语言和翻译添加具有相同 ID 的新实体,nhibernate 会产生一个错误,表明密钥已存在,我也理解它。

所以,我肯定走错了路,但如果有人能指出我如何实现我想要的映射的正确解决方案,我将不胜感激。

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

预先感谢您的帮助。

托马斯

I have the following data structure :

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

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

So I could have such a data with two joined tables :

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

From my domain point of view I care only about Resume entity. I don't want to have Resume entity with its collection of Resume_Translations because I would only have one Resume entity with a current translation.

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

My current mapping with Fluent NHibernate is as follows :

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);
                        });
    }
}

I can get what I want from the repository without problem just passing in the WHERE predicate the Id of Resume and the Language I want to.

However I have some problems with Inserting and Updating the values.

My question is: How I would define a mapping that NHibernate Inserts a new record only in Resume_Translation table instead of Updating the record for the current entity ?

So what I want to achieve is if I have in the database the following record :

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

Join is good for one to one relationship between tables so if I get this into my entity and I change the language and translation, nhibernate is performing an update and I can understand it. If I try to add a new entity with the same Id by different language and translation, nhibernate yields an error that a key already exists and I understand it also.

So, certainly I'm going down the wrong path, but If some one could point me to the correct solution on how I could achieve a mapping that I want I would greatly appreciate.

Another question, how do you deal with a entities and theirs translations from the business point of view ?

Thanks, in advance for your help.

Thomas

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浅紫色的梦幻 2024-11-26 15:21:24

斯特凡走在正确的道路上。我已经调整了他的建议,建立一个双向关联,这将使更新变得更加容易。这种方法的一个问题是,您需要在插入时手动分配 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();
    }
}

Stefan is on the right track. I've tweaked his suggestion to have a bi-directional association which would make updating a lot easier. One catch with this approach is that you need to manually assign the Resume property of the ResumeTranslation instance when inserting so that NHibernate will properly assign the Resume table key to the ResumeTranslation row. So, given the associations you are mapping, this is how it would look in 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();
    }
}
寄居者 2024-11-26 15:21:24

对我来说似乎是一对多的关系。我个人会在我的 Resume 对象中拥有一个 ResumeTranslation 对象的集合。然后我会将其映射为标准的一对多。

然后,您可以向代表您的当前翻译Resume实体添加另一个属性ActiveResumeTranslation

Seems like a one to many relationship to me. I would personally have a collection of ResumeTranslation objects within my Resume object. I would then map this as a standard one to many.

You could then add another property ActiveResumeTranslation to your Resume entity that is representative of your current translation.

阳光的暖冬 2024-11-26 15:21:24

使用字典,使用语言作为键怎么样?

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 (抱歉,我没有使用 Fluent,所以不要问我它会是什么样子)。它将与您的数据库模型完全匹配。

What about using a dictionary, using the language as a key?

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

And map it accordingly as a map with a composite-element (sorry, I'm not using fluent, so don't ask me how it would look like). It would exactly match your database model.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文