2010-09-03 165 views
10

JPA 2是否有任何運行遞歸查詢的機制?遞歸JPA查詢?

這是我的情況:我有一個實體E,它包含一個整數字段x。它也可能有E類型的孩子,通過@OneToMany進行映射。我想要做的是通過主鍵找到一個E,並且得到它的值x以及所有後代的x值。有沒有辦法在單個查詢中做到這一點?

我使用的是Hibernate 3.5.3,但我不希望在Hibernate API上有明確的依賴關係。


編輯:據this項目,Hibernate並不有這個功能,或者至少是在三月份沒有。所以JPA似乎不太可能,但我想確定一下。

回答

21

使用簡單的鄰接模型其中每行包含對其父代的引用,這將引用同一表中的另一行,但與JPA無法很好地協作。這是因爲JPA不支持使用Oracle CONNECT BY子句或SQL標準WITH語句生成查詢。如果沒有這兩個條款中的任何一個,就不可能使鄰接模型成爲有用的。

但是,還有其他一些方法可用於解決可應用於此問題的此問題。第一個是物化路徑模型。這是將節點的完整路徑平鋪爲單個列的位置。表定義延伸,就像這樣:

CREATE TABLE node (id INTEGER, 
        path VARCHAR, 
        parent_id INTEGER REFERENCES node(id)); 

要插入節點的樹看起來有些東西一樣:

INSERT INTO node VALUES (1, '1', NULL); -- Root Node 
INSERT INTO node VALUES (2, '1.2', 1); -- 1st Child of '1' 
INSERT INTO node VALUES (3, '1.3', 1); -- 2nd Child of '1' 
INSERT INTO node VALUES (4, '1.3.4', 3); -- Child of '3' 

因此,要獲得節點'及其所有孩子的查詢是:

SELECT * FROM node WHERE id = 1 OR path LIKE '1.%'; 

要將此映射到JPA,只需將'path'列設置爲持久對象的屬性即可。然而,您必須做好記錄,以保持「路徑」字段保持最新。 JPA/Hibernate不會爲你做這件事。例如。如果將節點移動到其他父級,則必須更新父級引用並從新父級對象中確定新路徑值。

另一種方法被稱爲嵌套集模型,這有點複雜。可能最好由其創始者(而不是我逐字添加)最好described

還有第三種方法稱爲嵌套間隔模型,但是這嚴重依賴存儲過程來實現。

The Art of SQL的第7章描述了對這個問題更完整的解釋。

+0

這比我所尋找的要複雜得多,但謝謝你提供的信息。對物化路徑模型還有一個更簡單的替代方法 - 讓每個節點存儲它所屬樹的根節點的ID。這意味着你只能使用根節點作爲查詢的起點,但在我的具體情況下這不是問題 - 所以這可能是我會做的。 – 2010-09-03 22:08:12

+0

您可以使用通過JPA管理的鄰接列表,但使用本機查詢來使用遞歸SQL功能。這會有點噁心,而且不便攜,但它會是兩全其美的。嵌套集合是可移植的,但很煩瑣,而且對於一些相當常見的查詢來說效果不佳。 – 2010-09-04 00:11:14

+0

有什麼更改JPA 2.0 ...例如,我已經使用本地NamedQuery在我的查詢中有一個'CONNECT BY'子句。我已經嘗試過了,它確實會返回孩子,但不是以hierchical列表形式返回......只是平坦的孩子。有任何想法嗎? – SoftwareSavant 2012-06-27 15:00:25

4

在這篇文章中的最佳答案看起來像一個巨大的解決方法黑客給我。我已經不得不處理數據模型,輝煌的工程師們認爲將數據庫字段中的Tree Hiarchies編碼爲文本如「歐洲|英國| Shop1 | John」以及這些表中海量數據是一個好主意。不要驚慌,MyHackedTreeField LIKE'parentHierharchy%'這個形式的查詢表現在哪裏殺手。 解決這類問題最終需要在樹狀結構的內存緩存中創建以及許多其他類型......

如果您需要運行遞歸查詢並且您的數據量不是很大......讓您的生活變得簡單,並簡單地加載您需要運行計劃的數據庫字段。並用java編寫你的遞歸代碼。 除非你有充分的理由去做,否則不要在數據庫中創建它。

即使您的數據量很大,您很可能會將您的問題細分爲獨立的遞歸樹形批處理,並且無需一次加載所有數據。