Fluent NHibernate:映射复杂的多对多(带有附加列)并设置获取
我需要一个 Fluent NHibernate 映射来满足以下要求(如果没有别的事情,我还将采用适当的 NHibernate XML 映射并对其进行逆向工程)。
详细信息
我在两个实体之间存在多对多关系:Parent
和 Child
。这是通过一个额外的表来存储父级和子级的身份来完成的。但是,我还需要在该映射上定义两个附加列,以提供有关关系的更多信息。
这大致就是我定义类型的方式,至少是相关部分(其中 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我不明白为什么它不能按照您的方式工作,但我可以告诉您如何映射它:
为了提高性能,请尝试使其通过联接获取多对一:
I don't understand why it doesn't work the way you do it, but I can tell you how I would map it:
To enhance performance, try to make it fetch the many-to-one by a join: