Fluent NHibernate:映射复杂的多对多(带有附加列)并设置获取

发布于 2024-08-29 10:41:57 字数 3915 浏览 3 评论 0原文

我需要一个 Fluent NHibernate 映射来满足以下要求(如果没有别的事情,我还将采用适当的 NHibernate XML 映射并对其进行逆向工程)。


详细信息

我在两个实体之间存在多对多关系:ParentChild。这是通过一个额外的表来存储父级和子级的身份来完成的。但是,我还需要在该映射上定义两个附加列,以提供有关关系的更多信息。

这大致就是我定义类型的方式,至少是相关部分(其中 Entity 是一些基本类型,它提供 Id 属性并根据该 Id 检查等效性):

public class Parent : Entity
{
    public virtual IList<ParentChildRelationship> Children { get; protected set; }

    public virtual void AddChildRelationship(Child child, int customerId)
    {
       var relationship = new ParentChildRelationship
                        {
                           CustomerId = customerId,
                           Parent = this,
                           Child = child
                        };
       if (Children == null) Children = new List<ParentChildRelationship>();
       if (Children.Contains(relationship)) return;
       relationship.Sequence = Children.Count;
       Children.Add(relationship);
    }
}

public class Child : Entity
{
    // child doesn't care about its relationships
}

public class ParentChildRelationship
{
    public int CustomerId { get; set; }
    public Parent Parent { get; set; }
    public Child Child { get; set; }
    public int Sequence { get; set; }

    public override bool Equals(object obj)
    {
       if (ReferenceEquals(null, obj)) return false;
       if (ReferenceEquals(this, obj)) return true;
       var other = obj as ParentChildRelationship;
       if (return other == null) return false;

       return (CustomerId == other.CustomerId
           && Parent == other.Parent
           && Child == other.Child);
    }

    public override int GetHashCode()
    {
       unchecked
       {
           int result = CustomerId;
           result = Parent == null ? 0 : (result*397) ^ Parent.GetHashCode();
           result = Child == null ? 0 : (result*397) ^ Child.GetHashCode();
           return result;
       }
    }
}

数据库中的表看起来大约像(假设主/外键和宽恕语法):

create table Parent (
   id int identity(1,1) not null
)

create table Child (
   id int identity(1,1) not null
)

create table ParentChildRelationship (
   customerId int not null,
   parent_id int not null,
   child_id int not null,
   sequence int not null
)

我同意 Parent.Children 是延迟加载的属性。但是,ParentChildRelationship 应立即加载 ParentChildRelationship.Child。此外,我想在急切加载时使用 Join。

SQL,当访问 Parent.Children 时,NHibernate 应该生成一个等效的查询:

SELECT * FROM ParentChildRelationship rel LEFT OUTER JOIN Child ch ON rel.child_id = ch.id WHERE Parent_id = ?

OK,所以这样做我的映射看起来像这样:

ParentMap : ClassMap<Parent>
{
   public ParentMap()
   {
      Table("Parent");
      Id(c => c.Id).GeneratedBy.Identity();
      HasMany(c => c.Children).KeyColumn("parent_id");
    }
}

ChildMap : ClassMap<Child>
{
   public ChildMap()
   {
      Table("Child");
      Id(c => c.Id).GeneratedBy.Identity();
   }
}


ParentChildRelationshipMap : ClassMap<ParentChildRelationship>
{
   public ParentChildRelationshipMap()
   {
      Table("ParentChildRelationship");
      CompositeId()
                .KeyProperty(c => c.CustomerId, "customerId")
                .KeyReference(c => c.Parent, "parent_id")
                .KeyReference(c => c.Child, "child_id");
      Map(c => c.Sequence).Not.Nullable();
    }
}

所以,在我的测试中,如果我尝试获取 myParentRepo.Get(1).Children,它实际上会获取我所有的关系,并且当我访问时它们来自关系、Child 对象(例如,我可以通过执行 parent.Children.Select(r => r.Child).ToList() 来获取所有它们)。

然而,NHibernate 生成的 SQL 效率很低。当我访问parent.Children时,NHIbernate对每个关系中的每个子项执行SELECT * FROM ParentChildRelationship WHEREparent_id = 1,然后执行SELECT * FROM Child WHERE id = ?。我理解 NHibernate 这样做的原因,但我不知道如何设置映射以使 NHibernate 按照我上面提到的方式进行查询。

I need a Fluent NHibernate mapping that will fulfill the following (if nothing else, I'll also take the appropriate NHibernate XML mapping and reverse engineer it).


DETAILS

I have a many-to-many relationship between two entities: Parent and Child. That is accomplished by an additional table to store the identities of the Parent and Child. However, I also need to define two additional columns on that mapping that provide more information about the relationship.

This is roughly how I've defined my types, at least the relevant parts (where Entity is some base type that provides an Id property and checks for equivalence based on that Id):

public class Parent : Entity
{
    public virtual IList<ParentChildRelationship> Children { get; protected set; }

    public virtual void AddChildRelationship(Child child, int customerId)
    {
       var relationship = new ParentChildRelationship
                        {
                           CustomerId = customerId,
                           Parent = this,
                           Child = child
                        };
       if (Children == null) Children = new List<ParentChildRelationship>();
       if (Children.Contains(relationship)) return;
       relationship.Sequence = Children.Count;
       Children.Add(relationship);
    }
}

public class Child : Entity
{
    // child doesn't care about its relationships
}

public class ParentChildRelationship
{
    public int CustomerId { get; set; }
    public Parent Parent { get; set; }
    public Child Child { get; set; }
    public int Sequence { get; set; }

    public override bool Equals(object obj)
    {
       if (ReferenceEquals(null, obj)) return false;
       if (ReferenceEquals(this, obj)) return true;
       var other = obj as ParentChildRelationship;
       if (return other == null) return false;

       return (CustomerId == other.CustomerId
           && Parent == other.Parent
           && Child == other.Child);
    }

    public override int GetHashCode()
    {
       unchecked
       {
           int result = CustomerId;
           result = Parent == null ? 0 : (result*397) ^ Parent.GetHashCode();
           result = Child == null ? 0 : (result*397) ^ Child.GetHashCode();
           return result;
       }
    }
}

The tables in the database look approximately like (assume primary/foreign keys and forgive syntax):

create table Parent (
   id int identity(1,1) not null
)

create table Child (
   id int identity(1,1) not null
)

create table ParentChildRelationship (
   customerId int not null,
   parent_id int not null,
   child_id int not null,
   sequence int not null
)

I'm OK with Parent.Children being a lazy loaded property. However, the ParentChildRelationship should eager load ParentChildRelationship.Child. Furthermore, I want to use a Join when I eager load.

The SQL, when accessing Parent.Children, NHibernate should generate an equivalent query to:

SELECT * FROM ParentChildRelationship rel LEFT OUTER JOIN Child ch ON rel.child_id = ch.id WHERE parent_id = ?

OK, so to do that I have mappings that look like this:

ParentMap : ClassMap<Parent>
{
   public ParentMap()
   {
      Table("Parent");
      Id(c => c.Id).GeneratedBy.Identity();
      HasMany(c => c.Children).KeyColumn("parent_id");
    }
}

ChildMap : ClassMap<Child>
{
   public ChildMap()
   {
      Table("Child");
      Id(c => c.Id).GeneratedBy.Identity();
   }
}


ParentChildRelationshipMap : ClassMap<ParentChildRelationship>
{
   public ParentChildRelationshipMap()
   {
      Table("ParentChildRelationship");
      CompositeId()
                .KeyProperty(c => c.CustomerId, "customerId")
                .KeyReference(c => c.Parent, "parent_id")
                .KeyReference(c => c.Child, "child_id");
      Map(c => c.Sequence).Not.Nullable();
    }
}

So, in my test if i try to get myParentRepo.Get(1).Children, it does in fact get me all the relationships and, as I access them from the relationship, the Child objects (for example, I can grab them all by doing parent.Children.Select(r => r.Child).ToList()).

However, the SQL that NHibernate is generating is inefficient. When I access parent.Children, NHIbernate does a SELECT * FROM ParentChildRelationship WHERE parent_id = 1 and then a SELECT * FROM Child WHERE id = ? for each child in each relationship. I understand why NHibernate is doing this, but I can't figure out how to set up the mapping to make NHibernate query the way I mentioned above.

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

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

发布评论

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

评论(1

梦纸 2024-09-05 10:41:57

我不明白为什么它不能按照您的方式工作,但我可以告诉您如何映射它:

<class name="Parent">

    <id .../>

    <list name="Children" table="ParentChildRelationship">
        <key column="parent_id"/>
        <index column="Sequence"/>

        <composite-element>
            <property name="CustomerId"/>
            <many-to-one name="Child"/>
        </composite-element>
    </list>

</class>

<class name="Child">
    <id .../>
    <property .../>
</class>

为了提高性能,请尝试使其通过联接获取多对一:

      <many-to-one name="Child" fetch="join" />

I don't understand why it doesn't work the way you do it, but I can tell you how I would map it:

<class name="Parent">

    <id .../>

    <list name="Children" table="ParentChildRelationship">
        <key column="parent_id"/>
        <index column="Sequence"/>

        <composite-element>
            <property name="CustomerId"/>
            <many-to-one name="Child"/>
        </composite-element>
    </list>

</class>

<class name="Child">
    <id .../>
    <property .../>
</class>

To enhance performance, try to make it fetch the many-to-one by a join:

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