4

我的模型包含一个类,该类具有本节一部分Section的有序列表。Statics忽略所有其他属性,模型的实现如下所示:

public class Section
{
    public virtual int Id { get; private set; }
    public virtual IList<Static> Statics { get; private set; }
}

public class Static
{
    public virtual int Id { get; private set; }
}

在数据库中,关系被实现为一对多,其中表Static 有一个指向的外键Section和一个整数列Position,用于存储其在列表中的索引位置。

映射在 Fluent NHibernate 中完成,如下所示:

public SectionMap()
{
    Id(x => x.Id);
    HasMany(x => x.Statics).Cascade.All().LazyLoad()
            .AsList(x => x.WithColumn("Position"));
}

public StaticMap()
{
    Id(x => x.Id);
    References(x => x.Section);
}

现在我可以加载现有Static的 s,并且还可以更新它们的详细信息。但是,我似乎找不到将新Statics 添加到 a的方法Section,并将此更改保留到数据库中。我尝试了几种组合:

  • mySection.Statics.Add(myStatic)
  • session.Update(mySection)
  • session.Save(myStatic)

但我得到的最接近的(使用前两个语句)是 SQL 异常读取:“无法将值 NULL 插入到列 'Position'”。显然INSERT这里尝试了 an,但 NHibernate 似乎不会自动将索引位置附加到 SQL 语句中。

我究竟做错了什么?我在映射中遗漏了什么吗?我是否需要将Position列公开为属性并自己为其赋值?

编辑:如果我删除数据库中列的NOT NULL约束,显然一切都按预期工作。Static.Position我猜 NHibernate 会在使用值更新行后立即进行插入Position

虽然这是对问题的回答,但我不确定它是否是最好的。我希望该Position列不可为空,因此我仍然希望有某种方法可以使 NHibernate 直接在INSERT语句中为该列提供值。

因此,这个问题仍然悬而未决。还有其他解决方案吗?

4

3 回答 3

7

在 NHibernate 中使用双向一对多关系时,端点之一必须是“反向的”。最佳实践是将集合的结尾设置为反向,因为这样可以避免不必要的 SQL 语句并允许 id 列为“非空”。

在文档的第 6.4 节中,您可以找到以下注释:

非常重要的注意事项:如果关联的列被声明为 NOT NULL,NHibernate 在创建或更新关联时可能会导致违反约束。为防止此问题,您必须使用与标记为 inverse="true" 的多值端(集合或包)的双向关联。请参阅本章后面对双向关联的讨论。

因此,您需要将 .Inverse() 添加到 SectionMap 中的 HasMany 映射中。

public SectionMap()
{
    Id(x => x.Id);
    HasMany(x => x.Statics)
        .Cascade.All()
        .LazyLoad()
        .Inverse()
        .AsList(x => x.WithColumn("Position"));
}

您可能还需要在 Section 上使用 Add 和 Remove 方法,该方法设置/重置静态的引用以及将静态添加到/从其自己的集合中删除:

public virtual void AddStatic(Static static)
{
    Statics.Add(static);
    static.Section = this;
}


public virtual void RemoveStatic(Static static)
{
    Statics.Remove(static);
    static.Section = null;
}

这些方法确保参考在关系的双方都保持准确。

根据文档 NHibernate 的第 6.8 节,在使用索引集合时不支持双向关系:

请注意,NHibernate 不支持将索引集合(列表、映射或数组)作为“多”端的双向一对多关联,您必须使用集合或包映射。

因此,如果您仍然遇到问题,请考虑使用单向关系而不是双向关系,但这可能意味着您的外键列需要为空(根据帖子开头的注释)。否则,您可能必须将您的收藏映射为一个包或集合而不是列表。

于 2009-08-10T08:37:07.970 回答
2

在静态表中,您有一个名为“SectionID”或类似名称的字段。让这个字段为 NULLable:NHibernate 首先添加新记录,然后更新引用的 id。

我在我的数据库中发现了这一点,我也很惊讶:为什么 NH 不喜欢数据库级别的正确表引用?

于 2009-08-10T07:26:18.940 回答
1

我想我会投入我的 0.02 美元,只是因为我来这里是为了解决你的问题,但似乎没有一个答案真正奏效。

您的解决方案几乎就在那里。您对 HasMany 的映射是正确的。问题是,正如您所说,Position 没有在数据库中更新(当您将其设置为 null 时,FWIW 仅“有效”,因为 NULL 被插入到数据库中;对我来说,这并没有真正起作用;) )。

我觉得有点奇怪,你也需要在你的静态类上映射位置,并且显然在静态上包含一个位置属性。但是,您不希望 Position 属性是可写的,因为它的值取决于 Static 在列表中的位置。

将以下内容添加到静态

public virtual int Position 
{
    get 
    {
        // Will throw exception if Section is null
        // or Section.Statics is null...
        return Section.Statics.IndexOf(this);
    }
    protected set 
    {
    }
}

数据库中的 Position 列中的内容将被更新(请记住在您的 StaticMap 中映射您的位置)。

我猜静态的 NHib 代理可以根据它在列表中的位置(通过一些比我拥有的更大的魔法)更新 Position 字段,然后将其保存到数据库中。

于 2013-01-24T01:18:38.107 回答