2011-09-07 48 views
2

我想从数据库加载一个父/子对象(类似于Java的DefaultMutableTreeNode对象)的图形。在2之间有一个简单的一对多关联。图的层次总数是已知的,所以我确切地知道调用'getChildren()'方法的次数。 我想要做的是不要为实际叶节点调用此方法。通常该图由几个非叶节点和几百个叶节点组成。如果我在hb映射中指定lazy=false,我会从叶节点的子节点获得数百个不必要的hb查询,而我事先知道它们不是必需的(因为我知道树上的层级总数)。 不幸的是,我不能使用lazy=true,并且只能循环到叶节点的父节点,因为我正在断开客户机/服务器模型并使用beanlib加载整个对象图(包含多个其他对象)。加载与休眠控制叶子父/子层次

所以我试图找到一种方法来拦截加载的'儿童'集合,并指示HB停止时,它到达叶节点。有没有办法做到这一点? 我正在看2解决方案: 我想到的是这样的:当我呼吁node.getChildren()方法(在hb会话中),通常hb将执行一个数据库查询来获取孩子:有没有一种方法来拦截此调用只是没有做到?我知道没有孩子,所以我只是希望它快速失败(实际上我不想做它)。

谢谢 科斯塔斯

回答

1

你为什么不只是使用一个布尔leaf财产,并作出是否leaf是真实的你getChildren方法返回一个空列表?

private boolean leaf; 

private List<Node> children; 

public List<Node> getChildren() { 
    if (leaf) { 
     return Collection.<Node>emptyList(); 
    } 
    return children; 
} 
+0

你会把孩子们的收藏映射为懒惰吗? – meriton

+0

是的。如果你将某些东西映射为渴望的东西,它总是会被Hibernate加载。 –

+0

这不会解决问题:Hibernate仍然会打到数据库并尝试加载集合。问题是停止休眠从而这样做的叶节点。 @meriton:懒惰或没有关系,如果我得到一个解决方案;如果我找到一种方法来'拦截'db的调用并'停止'它。 – Costas

0

除非你的数据库是用Java代码issueing这些查询同一位置,它可能是一个性能瓶颈发出每个节点的查询,即使它只是每个节点内的查询。既然你知道树的最高水平(假设3为例的缘故),下面应该在单个查询来获取整个树:

from Node n1 
left join n1.children as n2 
left join n2.children as n3 
left join n3.children as n4 

这种方法的缺点是,结果集可能为每个后代重复每个内部节点的数据,即带宽乘以树层数。如果这是一个问题,因为你有很多等级,你可以启用批量抓取该集合,甚至用手做同样的事情:

List<Node> border = Collections.singletonList(rootNode); 
while (!border.isEmpty()) { 
    List<Integer> ids = new ArrayList<Integer>(); 
    for (Node n : border) { 
     ids.add(n.getId()); 
    } 

    // initialize the children collection in all nodes in border 
    session.createQuery("from Node n left join n.children where n.id in ?").setParameter(0, ids).list(); 

    List<Node> newBorder = new ArrayList<Node>(); 
    for (Node n : border) { 
     newBorder.addAll(n.getChildren()); 
    } 
    border = newBorder; 
} 

因为有树的水平这将发出许多查询,将每个节点的数据传输两次。 (有些数据库限制了in-clause的大小,那么你必须在该级别内进行批处理),然后

+0

这是一个解决方案,将适用于这个单表。事实上,对于单个节点级别还有大约20个内部/外部连接发生,因此对所有级别(有3-4个)进行查询会创建一个拥有超过100个连接的怪物;我不想要这个。该图是这样的,内节点不是很多(例如10-20),而叶节点是很多(例如500)。因此,发出10-20个查询是有意义的(而每个查询包含约20个与其他表的连接)。 – Costas

+0

我的第二个解决方案每个查询只有一个节点连接,所以这不应该成为问题。或者,您可以首先使用我的第一个方法获取所有节点,然后在单独的查询中连接其他表。 – meriton

+0

嗯,我知道这是一个加载所有级别的解决方案,但它不是原来问题的解决方案。谢谢你,虽然 – Costas

0

你可以在getChildren调用周围使用AOP来做类似的事情(请注意,这是非常粗糙的伪代码,你将不得不填补“空白”):

childrenResult = node.getChildren() 
if (Hibernate.isInitialized(childrenResult)) { 
    return node.getChildren() 
} else { 
    // Do something else here 
} 

这将完成,当你做出的getChildren一个呼叫和收集没有初始化,可以ingored或不准继续处理。但是,如果该项目已初始化,则将允许继续进行呼叫。关于Hibernate.isInitialized需要注意的一点是,它将在所有对象上返回true,但是尚未填充的延迟加载的集合。

如果您无法使用AOP,您可以随时在您的代码中对自己的getChildren调用进行检查。