我正在尝试使用NHibernate将使用以下entites构建的图形映射到关系数据库(代码不完整,仅用于演示目的,这应该看起来很简单)。潜在的Node和Edge类可能有子类,并且已经定义了一个继承自Node类的子类。对于此模型中的所有继承关系,使用的映射类型是join-subclass(table-per-subclass);
class GraphObject { ... }
class Node : GraphObject {
List<Edge> IngoingEdges;
List<Edge> OutgoingEdges;
}
class Edge : GraphObject {
Node StartNode { get; set; }
Node EndNode { get; set; }
}
对于节点和边缘之间的连接,使用双重多对一映射,如下所示,
从Edge.StartNode到节点(.OutgoingEdges)的多对一; 从Edge.EndNode到节点(.IngoingEdges)的多对一
由于需要在我们的项目中处理大量数据(数百万个节点和边缘),我们希望既能保持NHibernate提供的优势,又能最大限度地降低性能问题。不幸的是,似乎需要将近一个小时来保存或加载这样的模型。我目前正在做的是试图找出一种方法来完成一个语句中的加载,看看它需要多长时间。我做了一些尝试,我使用NHibernate Profiler来跟踪NHibernate框架生成的SQL语句,比如从数据持久性加载整个图形,但到目前为止我还没有设法消除那么大量的个体查询显然是为了确定哪些是特定边的开始和结束节点,看起来像
选择... StartNode为..,.. ID为..,...来自链接链接,其中link.StartNode = 10(表示节点ID的数字)
这意味着我有点受到所谓的N + 1问题的困扰。 那么有没有人遇到类似的问题,可以给我一些想法,无论是在本机SQL中还是通过其他方法改善这种特殊情况的性能。我真的很感激。有关点不清楚的任何问题也欢迎。
答案 0 :(得分:0)
StartNode
和EndNode
的延迟加载(在1个查询中获取Egde而不是3个)这会产生类似
的内容// initialize the collections efficiently
session.QueryOver<Node>()
.Where(n => n.Id == nodeId)
.Fetch(n => n.IngoingEdges)
.ToFuture();
firstNode = session.QueryOver<Node>()
.Where(n => n.Id == nodeId)
.Fetch(n => n.OutgoingEdges)
.ToFuture().Value;
var egdeIds = firstNode
.SelectMany(n => n.IngoingEdges)
.SelectMany(edge => new [] { edge.StartNode.Id, edge.EndNode.Id });
EagerLoadNode(nodeIds);
void EagerLoadNode(IEnumerable<int> nodeIds)
{
// initialize the collections efficiently
session.QueryOver<Node>()
.Where(n => n.Id.IsIn(nodeIds))
.Fetch(n => n.IngoingEdges)
.ToFuture();
firstNode = session.QueryOver<Node>()
.Where(n => n.Id.IsIn(nodeIds))
.Fetch(n => n.OutgoingEdges)
.ToFuture();
}