Re: [問題] 請問JPA如何做階層式查詢

作者: csieflyman (風之驕子)   2014-07-18 20:31:47
這個需求其實跟 LDAP 做的事情一樣
如果想要攤平成一個字串,建議可以使用 JDK 預帶的 LdapName
字串格式長得像這樣 OU=cc,OU=bb,OU=aa,DC=google,DC=com
http://docs.oracle.com/javase/7/docs/api/javax/naming/ldap/LdapName.html
http://docs.oracle.com/javase/7/docs/api/javax/naming/ldap/Rdn.html
你不必自創字串格式 而且LdapName格式是RFC標準 大家都看懂得字串意義
所有的字串prefix suffix add remove 動作都有method幫你處理
嫌不夠的話 還可以用 spring-ldap 的 LdapUtils LdapNameBuilder
http://projects.spring.io/spring-ldap/
至於底層資料儲存方式
如果用資料庫的話,我不太確定你查詢需求
不過可以參考 Interval Tree,它可以一個SQL把所有 subtree node 就找出來
http://en.wikipedia.org/wiki/Interval_tree
每個 node 都有 low hi 二個值
例如 where條件找 2 ~ 7 之間就可以查出 b,c,d
(1 a 10)
(2 b 7) (8 e 9)
(3 c 4) (5 d 6)
如果你有更多的查詢需求,例如往上往下找所有 node,甚至往上或往下找 n 層
那你可以參考以下的實作(Tree也是DAG) 這也是一個SQL 就查出來
http://www.codeproject.com/Articles/22824/A-Model-to-Represent-Directed-Acyclic-Graphs-DAG-o
如果你的資料量大而且階層多,建議用上述方式實作
否則一層一層爬資料下SQL,效能會變差
如果你不喜歡資料庫 覺得操作起來卡卡的
那你可以用 Apache Jackrabbit,操作資料本身的概念就是以階層式的方式思考
使用 Xpath Query 來查詢
http://jackrabbit.apache.org/
至於 In Memory Java 操作的話
實作Tree可以使用 javax.swing.tree 裡面的class
http://docs.oracle.com/javase/6/docs/api/javax/swing/tree/TreeNode.html
recursive…等操作都不必自己寫 而且你的類別也不必定義 parent child 欄位
如果你想要用 DAG 可以使用 JGrapht library
http://jgrapht.org/
有神人幫你實作好了 DirectedAcyclicGraph
http://jgrapht.org/javadoc/org/jgrapht/experimental/dag/DirectedAcyclicGraph.html
要注意以上都沒有 thread safe
如果有多個 root node 你可以多建立一個最上層的 dummy root node 串起來
※ 引述《popcorny (畢業了..@@")》之銘言:
: ※ 引述《NullLife (929rock化)》之銘言:
: : 其實最主要還是透過 yyc1217 板大的方法,將上面階層攤平成一個字串
: : 因為表的巢狀不太深,頂多3~5層,每層名字也不會太多頂多2~6個字
: : 然後我實際的情況會像是那一張表存放N多個公司的處部狀況,
: : 有多個根,可能處部完全重複,僅公司不同,例如:
: : seq | name | parent | company
: : 1 aa 甲
: : 2 bb 1 甲
: : 3 cc 2 甲
: : 4 dd 1 甲
: : 5 ee 4 甲
: : 6 ff 5 甲
: : 7 gg 甲
: : 8 hh 7 甲
: : 9 ii 8 甲
: : 10 aa 乙
: : 11 bb 10 乙
: : 12 cc 11 乙
: : 13 dd 10 乙
: : 14 ee 11 乙
: : 15 ff 14 乙
: : 麻煩是在有需求下distinct,找出所有所有處部情況
: : 因為這張表最早開給user自己去維護他們的分類,
: : 不過內部討論過後,決定不開給USER自行維護(避免太深等問題)
: : 由我們系統維護人員來幫客戶進行修改新增等
: : 因此分類表就可以獨立出來不跟公司掛勾,變成如下表:
: : seq | name | parent | parentStratum
: : 1 aa
: : 2 bb 1 aa
: : 3 cc 2 aa/bb
: : 4 dd 1 aa
: : 5 ee 4 aa/dd
: : 6 ff 5 aa/dd/ee
: : 7 gg
: : 8 hh 7 gg
: : 9 ii 8 gg/hh
: : 如此一來就不會有過多重複的 parentStratum
: : 也不用全查資料後下distinct找出所有分類情況,
: : 因為獨立出來的表就直接是結果了,
: : 然後在公司的表上面JoinTable來記該公司擁有哪些分類節點
: : 當然每家公司的分類情況必定存在分類表裡,
: : 如果沒有的話,就是新增某個支線之後,將他LINK給該公司
: : 所以想查Aa/Ba/Ca/Db...的時候,只要將分類串起來用like去找就好,
: : 因為分類表資料已經大幅縮減了,因此速度上會較之前設計的表快,
: : 然後公司若要調整分類結構,也不會像之前設計的表,非常難維護,
: : 只要變更LIKE表的結構,及調整完成。
: : 以上就是我們最後決定出來的模式,感謝各位。
: : PS:
: : 常常會很想說我要找出完美的設計,結果事實並不然,
: : 所以就只能在幾個方案中根據系統情況來使用較佳的方案 >"<
: 你的做法很ok
: 也就是我們常說的denormalize的做法..
: 另外一個是我認為也可行也是normalize的做法
: 但是需要資料庫的支援..
: 就是recursive query
: http://en.wikipedia.org/wiki/Hierarchical_and_recursive_queries_in_SQL
: 第一步還一樣先找出所有符合的資料 在你這邊就是部門名稱
:   第二部是針對第一步的每筆資料找出Path或是Root
: 可以用recusive的方式
: 我以MSSQL Server為例子
: with temp(seq, parent) as (
: (select seq, parent from Department where seq = :target_group)
: union all
: (
: select D.seq, D.parent
: from Department as D, temp
: where temp.parent = D.seq
: )
: )
: select * from temp where seq = :root_group
: with裡面會做recursive
: 第一個subquery是第一筆資料
: 第二個subquery是recusive去做直到到boundary
: 當然第一步如果有找到3比結果
: 就要3+1次query
: 但是可以方便你快速的追到你想要的root
: 或是你要做denormalize的地方
: 也可以用這個方法來建立出你的path
: 是有點複雜,就僅供參考 XD
作者: cyclone350 (老子我最神)   2014-07-18 22:23:00
推~ 沒一個知道的 XD
作者: NullLife (廢材大叔有點累)   2014-07-19 10:21:00
WOW 太感謝了!! 筆記筆記
作者: qrtt1 (有些事,有時候。。。)   2014-07-19 11:34:00
感謝分享

Links booklink

Contact Us: admin [ a t ] ucptt.com