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