Fluent-NHibernate 多对多级联不会填充链接表

发布于 2024-10-22 09:55:21 字数 1976 浏览 1 评论 0 原文

好的,无论我如何定义这些映射,我的多对多映射都不想与级联插入一起使用。我尝试了 Cascade() 与 Reverse() 的各种组合,并删除所有不必要的属性,只是为了了解它们是否与此不起作用但没有锁定有关。

这真的很简单:我有一个 Message (如电子邮件),它从用户(我将实体称为 BasicUser)发送到多个用户(通过属性)。对于收件人而言,UserMessage 是多对多关系,而 FromUser 是一对多关系。 FromUser 工作正常并且更新正常,但我的问题是多对多。我什至删除了 FromUser 和关系只是为了检查这是否是问题所在,但没有帮助。

所以这是表格设计(为了简单起见,删除了从 FromUserBasicUser 的关系)

以下是映射:

public class MessageMap : ClassMap<Message>
{

    public MessageMap()
    {
        Id(x => x.Id).Column("MessageId");
        Map(x => x.Subject);
        Map(x => x.SentAt);
        Map(x => x.Body);
        References(x => x.From).Column("FromUser");
        HasManyToMany(x => x.To).Table("BasicUserMessage").ChildKeyColumn("BasicUserId")
            .ParentKeyColumn("MessageId").Cascade().All();
    }
}

public class BasicUserMap : ClassMap<BasicUser>
{
    public BasicUserMap()
    {
        Id(x => x.Id).Column("BasicUserId");
        Map(x => x.DisplayName);
        Map(x => x.Username);
        HasManyToMany(x => x.Messages).Table("BasicUserMessage").ChildKeyColumn("MessageId")
            .ParentKeyColumn("BasicUserId").Inverse();
    }
}

我称之为此方法,但它不起作用(表 BasicUserMessage 未填充): (注意 ID 为 1、2 和 3 的用户确实存在 - 我也尝试从数据库获取它们,然后添加到列表仍然不起作用)

ISessionFactory factory = GetSessionFactory();
ISession session = factory.OpenSession();
Message m = new Message()
                {
                    Body = "Please note 2",
                    Subject = "Secret 2",
                    From = new BasicUser(){Id = 2},
                    SentAt = DateTime.Now,
                };
m.To.Add(new BasicUser(){Id = 1});
m.To.Add(new BasicUser(){Id=3});
session.SaveOrUpdate(m);
session.Close();

OK, no matter how I define these mappings, my many-to-many mapping does not want to work with cascade insert. I have tried various combination of Cascade() with Reverse() and removing all unnecessary properties just to understand if they had anything to do with this not working, but no lock.

It is really Simple stuff: I have a Message (like an email) which is sent from a user (I have called the entity BasicUser) to a number of users (through property To). User and Message in terms of recipients have a many-to-many relationship but FromUser has one-to-many. FromUser works fine and it is updated alright but my problem is with many-to-many. I even removed FromUser and relationship just to check if this was the problem, but did not help.

So here is the table design (Have removed the relationship from FromUser to BasicUser for simplicity)

enter image description here

And here are the mappings:

public class MessageMap : ClassMap<Message>
{

    public MessageMap()
    {
        Id(x => x.Id).Column("MessageId");
        Map(x => x.Subject);
        Map(x => x.SentAt);
        Map(x => x.Body);
        References(x => x.From).Column("FromUser");
        HasManyToMany(x => x.To).Table("BasicUserMessage").ChildKeyColumn("BasicUserId")
            .ParentKeyColumn("MessageId").Cascade().All();
    }
}

public class BasicUserMap : ClassMap<BasicUser>
{
    public BasicUserMap()
    {
        Id(x => x.Id).Column("BasicUserId");
        Map(x => x.DisplayName);
        Map(x => x.Username);
        HasManyToMany(x => x.Messages).Table("BasicUserMessage").ChildKeyColumn("MessageId")
            .ParentKeyColumn("BasicUserId").Inverse();
    }
}

And I call this and it does not work (table BasicUserMessage does not get populated):
(Note Users with Id 1, 2 and 3 do exist - I also tried getting them from database and then add to list still did not work)

ISessionFactory factory = GetSessionFactory();
ISession session = factory.OpenSession();
Message m = new Message()
                {
                    Body = "Please note 2",
                    Subject = "Secret 2",
                    From = new BasicUser(){Id = 2},
                    SentAt = DateTime.Now,
                };
m.To.Add(new BasicUser(){Id = 1});
m.To.Add(new BasicUser(){Id=3});
session.SaveOrUpdate(m);
session.Close();

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

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

发布评论

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

评论(4

划一舟意中人 2024-10-29 09:55:21

关于交易的答案是偶然发生的正确性。另外,偶然发生的正确情况是,这本身就是如此表现的,因为您正在使用类似于 IDENTITY 生成器的东西,该生成器需要在保存时进行数据库访问才能获取身份。

以下是当您在多对多关联上设置 save-update 级联(或任何暗示这一点的级联)时,NHibernate 会执行的操作:

保存父实体。由于身份策略,这会立即进入数据库。该集合已“修改”(因为它是新的),所以让我们看看它的成员。仅当未在父关系的映射上设置inverse 时,才会发生此步骤。在链接表中为它们每个创建一个条目。

但是等等,其中一些条目是暂时的。级联设置正确,所以没关系 - 但为了在会话中创建链接表条目,我们需要这些子项的 ID,所以让我们立即保存它们。

现在,所有相关实体都是持久的,并且会话对于链接表中的所有条目都有一个待插入。刷新会话将发出命令并创建这些条目。

当您将代码包装在事务中时,提交事务会刷新会话,以便在提交时创建会话。如果您使用不需要数据库往返的身份生成器,则链接表条目和实体都会同时插入,因此您不会看到所看到的“断开连接” - 如果会话永远不会被刷新,也不会插入任何内容,并且当刷新时,所有内容都会被插入。如果您没有这些东西,显式刷新会话将创建链接表条目,一切都会好起来的。

The answer about transactions is correct-by-incidental-occurrence. Also correct-by-incidental occurrence is that this is manifesting itself as such because you are using something like an IDENTITY generator that requires a database trip on save to obtain the identity.

Here is what NHibernate does when you set a save-update cascade (or any cascade which implies that) on a many-to-many association with such:

Save the parent entity. This goes to the database immediately because of the identity strategy. The collection is "modified" (because it's new), so let's look at it's members. This step only occurs if inverse is not set on the parent's mapping of the relationship. Create an entry in the link table for each of them.

But wait, some of these entries are transient. Cascades are set properly, so that's okay - but in order to create the link table entry in the session, we need the id of those children, so let's save them immediately.

Now all relevant entities are persistent, and the session has a pending insert for all of the entries in the link table. Flushing the session will issue the commands and create those entries.

When you wrap your code in a transaction, committing the transaction flushes the session, so that is created when you commit. If you use an identity generator that doesn't require a DB round-trip, then the link table entries and the entities are all inserted at the same time, so you won't see the "disconnect" that you're seeing - if the session is never flushed, nothing is ever inserted, and when it is flushed, everything is inserted. If you have neither of these things, flushing your session explicitly will create the link table entries and all will be well.

今天小雨转甜 2024-10-29 09:55:21

您将两个引用都颠倒了。这对NH意味着:不要从这一侧存储它,因为它已经被另一侧存储了。如果两者都相反,则不存储任何内容。

从参考文献之一中删除 Inverse。

You made both references inverse. This means to NH: don't store it from this side, because it is already stored by the other side. If both are inverse, nothing is stored.

Remove Inverse from one of the references.

困倦 2024-10-29 09:55:21

您需要将代码包装在事务中。否则 Nhibernate 不会在连接表中保存值

You need to wrap your code in transaction. Otherwise Nhibernate won't save values in joining table

情归归情 2024-10-29 09:55:21

您需要使 BasicUser 对象持久化:

ISessionFactory factory = GetSessionFactory();
ISession session = factory.OpenSession();
Message m = new Message()
                {
                    Body = "Please note 2",
                    Subject = "Secret 2",
                    From = new BasicUser(){Id = 2},
                    SentAt = DateTime.Now,
                };
var basicUser1 = new BasicUser(){Id = 1};
session.Save(basicUser1);
m.To.Add(basicUser1);
var basicUser3 = new BasicUser(){Id = 3};
session.Save(basicUser3);
m.To.Add(basicUser3);
session.Save(m);
session.Flush();

这当然应该在事务中完成(正如 Sly 回答的那样),并且会话应该包装在 using 语句中。

You need to make the BasicUser objects persistent:

ISessionFactory factory = GetSessionFactory();
ISession session = factory.OpenSession();
Message m = new Message()
                {
                    Body = "Please note 2",
                    Subject = "Secret 2",
                    From = new BasicUser(){Id = 2},
                    SentAt = DateTime.Now,
                };
var basicUser1 = new BasicUser(){Id = 1};
session.Save(basicUser1);
m.To.Add(basicUser1);
var basicUser3 = new BasicUser(){Id = 3};
session.Save(basicUser3);
m.To.Add(basicUser3);
session.Save(m);
session.Flush();

This should of course be done in a transaction (as Sly answered) and the session should be wrapped in a using statement.

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