理解外键not-null = true和反向行为与NHibernate的零对一关系

rbe*_*amy 11 nhibernate nhibernate-mapping

我正在尝试让NHibernate使用集合的许多方面来管理双向关联以建立零对一关系.

父类和地图:

public class Parent
{
    private ICollection<Child> children;
    public Parent()
    {
        this.children = new HashedSet<Child>();
    }
    public virtual Guid Id { get; protected internal set; }
    public virtual Child Child
    {
        get { return children.FirstOrDefault(); }
        set
        {
            {
                this.children.Clear();
                if (value != null)
                {
                    this.children.Add(value);
                }
            }
        }
    }
}

public class ParentMap : ClassMap<Parent>
{
    public ParentMap()
    {
        this.Id(x => x.Id)
            .GeneratedBy.GuidComb();
        this.HasMany<Child>(Reveal.Member<Parent>("children"))
            .Access.Field()
            .Cascade.All()
            .Not.Inverse()
            .AsSet();
    }
}
Run Code Online (Sandbox Code Playgroud)

儿童班和地图:

public class Child
{
    public virtual Guid Id { get; protected internal set; }
    public virtual Parent Parent { get; set; }
}

public class ChildMap : ClassMap<Child>
{
    public ChildMap()
    {
        this.Id(x => x.Id)
            .GeneratedBy.GuidComb();
        this.References(x => x.Parent)
            .Not.Nullable()
            .Cascade.All();
    }
}
Run Code Online (Sandbox Code Playgroud)

以下代码生成两个插入和更新:

var parent = new Parent();
var child = new Child();
parent.Child = child;
child.Parent = parent;
session.Save(parent);
session.Flush();
Run Code Online (Sandbox Code Playgroud)

注意第二个插入和以下更新的基本上重复的SQL:

exec sp_executesql N'INSERT INTO [Parent] (Id) VALUES (@p0)',N'@p0 uniqueidentifier',@p0='AA5A146E-E3F5-4373-B7A8-9EF301171401'
go
exec sp_executesql N'INSERT INTO [Child] (Parent_id, Id) VALUES (@p0, @p1)',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='AA5A146E-E3F5-4373-B7A8-9EF301171401',@p1='B78C4461-A217-47FC-BE02-9EF30117140A'
go
exec sp_executesql N'UPDATE [Child] SET Parent_id = @p0 WHERE Id = @p1',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='AA5A146E-E3F5-4373-B7A8-9EF301171401',@p1='B78C4461-A217-47FC-BE02-9EF30117140A'
go
Run Code Online (Sandbox Code Playgroud)

虽然这段代码产生臭名昭着not-null property references a null or transient value inverse:

var parent = new Parent();
var child = new Child();
parent.Child = child;
//child.Parent = parent;
session.Save(parent);
session.Flush();
Run Code Online (Sandbox Code Playgroud)

我发现这个职位很多,但还没有找到如何做零到一的,有一个明确的指导inverse=falseone侧.

我尝试过这里提到的一对多/一对一的方法.

同样,我在NHibernate上发现了几个关于(不)可以为空的外键的公开问题:NH-941,NH-1050等.

我究竟做错了什么?

编辑2011-05-30

所以,我的临时解决方案是inverse=true在多方面采用标准设置,并在父设置器中做一些魔术:

public virtual Child Child
{
    get { return children.FirstOrDefault(); }
    set
    {
        {
            this.children.Clear();
            if (value != null)
            {
                value.Parent = this;
                this.children.Add(value);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但我仍然由百思不得其解inverse=false的行为,这应该是相当于inverse=true上多到一侧(有趣的是,FluentNhibernate不允许的ManyToOnePart设置inverse=true这样的文章建议).

Dan*_*ing 32

何时使用inverse ="true | false"

inverse属性用于帮助NHibernate知道应该使用哪个关系来维持关系.非反面(请注意双重否定)是将持续存在的一面.如果任何一方都不是反向的,那么这种关系将持续两次,就像INSERT后面UPDATE你提供的一个例子一样.如果双方都是反向的,则关系将不会持续存在,因此正确设置反向非常重要.

我想以inverse下面的方式思考.我不知道这是否正式是它的工作方式,但它有助于我的世界有意义:

许多到一

many-to-one关系总是如此inverse="false".它们始终用于保持与数据库的关系.由于它们总是inverse="false"存在,因此无需指定它,因此NHibernate(以及Fluent NHibernate)不提供它的选项.

(我只遇到了一个我希望我可以指定inverse="true"的情况many-to-one.如果你有one-to-many list一方many-to-one在另一方,你应该能够list控制关系,以便NHibernate可以负责设置index你现在的价值观,你必须在子类中添加一个属性并index自己管理这些值.)

一到一个

one-to-one关系总是如此inverse="true".如果没有关系idmany-to-one关系的另一方,他们就永远不会存在,这将保持这种关系.由于inverse值始终相同,因此无需指定它,因此不支持指定它.

集合

像类别bag,list,set等可以或可以不是双向关系的一部分.如果它们本身存在(也许是一包字符串元素),那么它们需要inverse="false"(这是默认值),因为没有其他人负责持久化关系.但是,如果它们与另一种关系一起存在(如传统的一对多/多对一),则应将它们指定为inverse="true".

如果many-to-many集合中您在关系的任一侧有一个集合,请将其中一个标记为inverse="true",并将另一个作为默认值inverse="false".同样,关键是关系的一方必须是非反向的.你应该选择哪一方?例如,如果我们在用户和角色之间采用多对多关系,那么您可能拥有大量用户和一些角色.在我看来,你应该映射Role.Usersinverse="true"User.Roles控制关系,因为它是一个较小的数据集,它可能是你更关心的集合.

(事实上​​,我会犹豫是否要包含Role.Users在模型中.假设"客户"角色有100,000个用户.然后customerRole.Users是一个无法使用的延迟加载炸弹等待爆炸.)

...回到你的问题......

由于关系的哪一侧是反向的并不重要,只要一方是非反向的,那么你应该把one-to-one方面作为反面,因为这是NHibernate想要的方式.不要在无关紧要的事情上对抗工具.在您提供的映射中,关系的两侧基本上都标记为非反向,这导致关系持续两次.以下映射应该更适合您:

public class Parent
{
    public virtual Guid Id { get; set; }
    public virtual Child Child { get; set; }
}

public class ParentClassMap : ClassMap<Parent>
{
    public ParentClassMap()
    {
        Id(x => x.Id);
        HasOne(x => x.Child)
            .PropertyRef(x => x.Parent)
            .Cascade.All();
    }
}

public class Child
{
    public virtual Guid Id { get; set; }
    public virtual Parent Parent { get; set; }
}

public class ChildClassMap : ClassMap<Child>
{
    public ChildClassMap()
    {
        Id(x => x.Id);
        References(x => x.Parent)
            .Not.Nullable()
            .Unique()
            .Cascade.SaveUpdate();
    }
}
Run Code Online (Sandbox Code Playgroud)

...导致测试插入代码中出现以下SQL:

exec sp_executesql N'INSERT INTO [Parent] (Id) VALUES (@p0)',N'@p0 uniqueidentifier',@p0='925237BE-558B-4985-BDA2-9F36000797F5'
exec sp_executesql N'INSERT INTO [Child] (Parent_id, Id) VALUES (@p0, @p1)',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='925237BE-558B-4985-BDA2-9F36000797F5',@p1='BE6D931A-8A05-4662-B5CD-9F36000797FF'
Run Code Online (Sandbox Code Playgroud)

没有更新查询!

  • +1,imo,反向的最佳解释我到目前为止已阅读:) (2认同)