流畅的NHibernate:映射复杂的多对多(带有附加列)和设置提取

时间:2010-04-19 22:56:11

标签: .net nhibernate fluent-nhibernate nhibernate-mapping many-to-many

我需要一个Fluent NHibernate映射来实现以下功能(如果没有别的,我还会采用适当的NHibernate XML映射并对其进行反向工程)。


详情

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

这大致是我如何定义我的类型,至少是相关部分(其中Entity是一些提供Id属性的基类型,并根据该Id检查等价性): / p>

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。此外,我希望在我急切加载时使用加入。

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

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

好的,所以要做到这一点,我的映射看起来像这样:

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 WHERE parent_id = 1然后SELECT * FROM Child WHERE id = ?。我理解为什么NHibernate会这样做,但我无法弄清楚如何设置映射以按照我上面提到的方式进行NHibernate查询。

1 个答案:

答案 0 :(得分:2)

我不明白为什么它不像你那样工作,但我可以告诉你我将如何映射它:

<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" />