我在Hibernate中表示对象层次结构时遇到了一些麻烦。我一直在搜索,并没有找到任何这样或类似的例子 - 如果这是一个常见的问题,你有道歉。
我有两种类型,我想继续使用Hibernate:Groups和Items *通过名称和父母的组合唯一地识别群组 *这些组被安排在许多树中,这样每个组都有零个或一个父组 *每个项目可以是零个或多个组的成员。
理想情况下,我想要双向关系让我得到:
*项目是其成员的所有组
*所有属于特定组或其后代成员的项目
我还需要能够从顶部遍历组树,以便在UI上显示它。
理想情况下,基本对象结构如下所示:
class Group {
...
/** @return all items in this group and its descendants */
Set<Item> getAllItems() { ... }
/** @return all direct children of this group */
Set<Group> getChildren() { ... }
...
}
class Item {
...
/** @return all groups that this Item is a direct member of */
Set<Group> getGroups() { ... }
...
}
最初,我刚刚在项目和组之间建立了一个简单的双向多对多关系,这样获取组层次结构中的所有项目都需要在树中递归,并且获取项目的组是一个简单的getter ,即:
class Group {
...
private Set<Item> items;
private Set<Group> children;
...
/** @return all items in this group and its descendants */
Set<Item> getAllItems() {
Set<Item> allItems = new HashSet<Item>();
allItems.addAll(this.items);
for(Group child : this.getChildren()) {
allItems.addAll(child.getAllItems());
}
return allItems;
}
/** @return all direct children of this group */
Set<Group> getChildren() {
return this.children;
}
...
}
class Item {
...
private Set<Group> groups;
/** @return all groups that this Item is a direct member of */
Set<Group> getGroups() {
return this.groups;
}
...
}
但是,这导致多个数据库请求获取具有许多后代的组中的项目,或者用于检索要在UI中显示的整个组树。这似乎非常低效,特别是对于更深,更大的组树。 在Hibernate中有没有更好或更标准的方式来表示这种关系?
我做了什么明显错误或愚蠢的事情?
到目前为止,我唯一的另一个想法是:
使用唯一的“path”字符串替换组的id,parent和name字段,该字符串指定组的整个祖先,例如:
/ rootGroup
/ rootGroup / aChild
/ rootGroup / aChild / aGrandChild
组和项之间的连接表将包含group_path和item_id。
这立即解决了我之前遇到的两个问题:
1.可以在单个查询中从数据库中提取整个组层次结构,并在内存中重建
2.要检索组或其后代中的所有项目,我们可以从group_item中选择group_path ='N'或group_path,如'N /%'
然而,这似乎打败了使用Hibernate的观点。欢迎所有的想法!
答案 0 :(得分:4)
我认为真正的问题是你希望性能如何受到影响。如果你宣布你所有的关系都是懒惰的,并且拉出你需要的东西,那么随着时间的推移你就会传播命中。在许多情况下,这意味着您的用户(人或硅)感知更快的反应时间。即使您一次处理整个图形,您实际上也不需要同时在内存中存储整个图形。
另一方面,拉动整个图表可以提高性能,同时加快操作速度。
两者之间最大的区别在于您的具体用例。如果这是一个Web服务器,将整个图形拉入内存将终止服务器。大多数人无论如何都无法处理超过7-10个选项,并且整个图表很容易让用户感到压力,因为选择使得UI难以导航。
还要记住这些优化规则:
答案 1 :(得分:3)
在我过去的项目中,我已经针对这个问题实施了两种不同但可比较的解决方案。
最重要的(从性能角度来看)解决方案是基于这样一个事实,即我拥有许多中等大小的不同树木。不是使用双向关系映射所有树叶,而是每个离开与树根相关,第二个与它们的直接父相关。
使用这个策略很容易识别所有根节点(FROM树t WHERE.parent IS NULL),并且很容易根据根元素获取完整的树(FROM树t WHERE t.root = :根)
“Set children”关系被声明为@Transient属性。使用简单的实用操作,可以随时重建原始树结构。
另一个解决方案涉及少量非常大的树结构。但幸运的是,这些结构的深度有限。我没有使用一个根,而是映射了几个(如果我没记错的话)6个“父母”级别。这样就很容易从数据库中获取和重建完整的子树。
(那些level1到level6的关系被映射为“懒惰的多对一”关系,应用程序严重依赖于二级缓存。)
答案 2 :(得分:2)
查看嵌套集。这是一篇好文章:
http://www.developersdex.com/gurus/articles/112.asp
基本上,您在修改层次结构时牺牲了一些性能,以简化深度检索,例如您希望将项目作为后代组的成员。
答案 3 :(得分:1)
我对获取节点子节点期间递归算法导致的高sql成本有同样的担忧。我决定在数据库中创建一个新表,它与原始表有多对一关系,并插入节点id的外键。下一栏是孩子的ID。使用这种结构,我在数据库表中保留了一次树结构。这样就不需要递归地获取子节点。我刚刚编写了一个连接两个表的SQL查询,并为我提供了特定节点的子节点。