NHibernate 在二次更新中设置外键,而不是在初始插入时违反键列上的 Not-Null 约束 [英] NHibernate sets Foreign Key in secondary update rather than on initial insert violating Not-Null Constrain on key column

查看:20
本文介绍了NHibernate 在二次更新中设置外键,而不是在初始插入时违反键列上的 Not-Null 约束的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对什么应该是相当简单的(我认为)NHibernate 用例有疑问.

I am having a problem with what should be a fairly simple (I would think) NHibernate use case.

我有一个像这样的经典父实体和子实体:

I have a classic Parent and a Child entity like so:

public class Parent 
{
    public virtual int ParentId { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<Child> Children { get; set; }
}

public class Child
{
    public virtual int ChildId { get; set; }
    public virtual Parent Parent { get; set; }
    public virtual string Name { get; set; }
}

和映射如下:

public class ParentMap : ClassMap<Parent>
{
    public ParentMap()
    {
        Id(x => x.ParentId).GeneratedBy.Native();
        Map(x => x.Name);
        HasMany(x => x.Children).KeyColumn("ParentId").Cascade.SaveUpdate();
    }
}

public class ChildMap : ClassMap<Child>
{
    public ChildMap()
    {
        Id(x => x.ChildId).GeneratedBy.Native();
        Map(x => x.Name);
        References(x => x.Parent).Column("ParentId").ReadOnly().Not.Nullable();
    }
}

最后,我有一个简单的测试:

Lastly, I have a simple tests:

   [Test]
    public void Test_save_family()
    {
        var parent = new Parent();
        var child = new Child {Parent = parent};
        parent.Children = new List<Child>{child};

        SessionManager.WithSession(
            session =>
                {
                    session.Save(parent);
                    session.Flush();
                });

    }

测试失败,出现 System.Data.SqlClient.SqlException:无法将 NULL 值插入列ParentId".这是正确的,因为该列不可为空,但为什么要插入空值?

The test fails with a System.Data.SqlClient.SqlException : Cannot insert the value NULL into column 'ParentId'. This is correct in that the column is non-nullable but why is it inserting null?

如果我删除空约束,保存会起作用,因为 NHibernate 首先插入父项,然后插入子项,然后更新子记录上的 ParentId 列,如下输出所示:

If I remove the null constraint, the save works because NHibernate first inserts the parent, then inserts the child, then updates the ParentId column on the child record as shown in this output:

NHibernate: INSERT INTO [Parent] (Name) VALUES (@p0); select SCOPE_IDENTITY();@p0 = NULL
NHibernate: INSERT INTO [Child] (Name) VALUES (@p0); select SCOPE_IDENTITY();@p0 = NULL
NHibernate: UPDATE [Child] SET ParentId = @p0 WHERE ChildId = @p1;@p0 = 2, @p1 = 1

这对我来说似乎很奇怪,因为在几乎所有情况下,此类外键列都被声明为不可为空,因此必须在插入时提供外键.那么为什么 NHibernate 没有在子行的初始插入时设置外键以及如何解决这个问题?

This seems bizarre to me as in almost all cases foreign key columns of this sort are declared non-nullable and hence the foreign key must be provided at insert. So why is NHibernate not setting the foreign key on the initial insert of the child row and how to I fix this?

推荐答案

你的映射的一些问题... 你有一个双向关系,NHibernate 需要知道用哪种方式更新它.在 OO 世界中,引用只有一种方式,NHibernate 无法知道 Parent->Children 与 Child->Parent 是相同的 FK.现在您已将 Child->Parent 设置为 ReadOnly().这告诉 NHibernate 不要更新这个属性.因此,它尝试插入 Child(具有空父级),然后从 Parent 端更新 FK.如果您的 FK 有非空约束,这将不起作用.通常的映射方式是在父端使用 Inverse=true ,让孩子担心持久性.(您在 OO 模型中的工作是确保 Parent->Children 集合包含与 Child->Parent 关系集相同的引用集.)

A few problems with your mapping... You have a bidirectional relationship and NHibernate needs to know which way to update it. In the OO world, references only go one way and there is no way for NHibernate to know that Parent->Children is the same FK as Child->Parent. Right now you have Child->Parent set to ReadOnly(). This is telling NHibernate not to update this property. So it tries to insert the Child (with a null parent) and then update the FK from the Parent side. This isn't working if you have a not null constraint on your FK. The usual way to map this is to use Inverse=true on the parent side and let the child worry about persistence. (It's your job in the OO model to ensure that the Parent->Children collection contains the same set of references as the set of Child->Parent relationships.)

public class ParentMap : ClassMap<Parent>
{
    public ParentMap()
    {
        Id(x => x.ParentId).GeneratedBy.Native();
        Map(x => x.Name);
        HasMany(x => x.Children).KeyColumn("ParentId").Inverse().Cascade.SaveUpdate();
    }
}

public class ChildMap : ClassMap<Child>
{
    public ChildMap()
    {
        Id(x => x.ChildId).GeneratedBy.Native();
        Map(x => x.Name);
        References(x => x.Parent).Column("ParentId").Not.Nullable();  // Removed ReadOnly()
    }
}

现在保存时发送到数据库的SQL语句为:

The SQL statements sent to the database when saving are now:

INSERT INTO [Parent]
           (Name)
VALUES     ('P1' /* @p0 */)
select SCOPE_IDENTITY()

INSERT INTO [Child]
           (Name,
            ParentId)
VALUES     ('C1' /* @p0 */,
            1 /* @p1 */)
select SCOPE_IDENTITY()

这篇关于NHibernate 在二次更新中设置外键,而不是在初始插入时违反键列上的 Not-Null 约束的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆