2011-09-07 49 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調用進行檢查。