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

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

问题描述



我有一个经典的Parent和一个Child实体,像这样:我有一个相当简单的(我认为)NHibernate用例的问题。 :

  public class Parent 
{
public virtual int ParentId {get;组; }
public virtual string Name {get;组; }
公共虚拟IList< Child>孩子{get;组; }
}

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



$ b $ $ $ $ $ $ $ $ $ $ b

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






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

  [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列中。这是正确的,该列是不可空的,但为什么它插入空?



如果我删除空约束,保存工作,因为NHibernate的第一次插入父,然后插入子项,然后更新子记录上的ParentId列,如下所示:

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

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

解决方案

你的映射存在一些问题...你有一个双向关系,NHibernate需要知道更新它的方法。在面向对象领域,引用只是一种方式,NHibernate没有办法知道Parent-> Children和Child-> Parent是同一个FK。现在你的Child-> Parent被设置为ReadOnly()。这是告诉NHibernate不要更新这个属性。所以它试图插入子(与一个空的父母),然后从父侧更新FK。如果你的FK没有空的约束,这是行不通的。映射这种方法的常用方法是在父节点上使用Inverse = true,并让子节点担心持久性。 (这是您在OO模型中的工作,以确保Parent-> Children集合包含与Child-> Parent关系集合相同的一组引用。)

  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);
引用(x => x.Parent).Column(ParentId)。Not.Nullable(); //删除ReadOnly()
}
}

发送给现在保存数据库:

pre $ INSERT INTO $ {
$($)
VALUES(' (')
选择SCOPE_IDENTITY()

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


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

And mappings as follows:

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

    }

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?

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

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?

解决方案

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

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设置外键在二次更新而不是在初始插入违反非空约束的关键列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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