Efficient representation of Hierarchies in Hibernate.
- by Alison G
I'm having some trouble representing an object hierarchy in Hibernate. I've searched around, and haven't managed to find any examples doing this or similar - you have my apologies if this is a common question.
I have two types which I'd like to persist using Hibernate: Groups and Items.
* Groups are identified uniquely by a combination of their name and their parent.
* The groups are arranged in a number of trees, such that every Group has zero or one parent Group.
* Each Item can be a member of zero or more Groups.
Ideally, I'd like a bi-directional relationship allowing me to get:
* all Groups that an Item is a member of
* all Items that are a member of a particular Group or its descendants.
I also need to be able to traverse the Group tree from the top in order to display it on the UI.
The basic object structure would ideally look like this:
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() { ... }
...
}
Originally, I had just made a simple bi-directional many-to-many relationship between Items and Groups, such that fetching all items in a group hierarchy required recursion down the tree, and fetching groups for an Item was a simple getter, i.e.:
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;
}
...
}
However, this resulted in multiple database requests to fetch the Items in a Group with many descendants, or for retrieving the entire Group tree to display in the UI. This seems very inefficient, especially with deeper, larger group trees.
Is there a better or standard way of representing this relationship in Hibernate?
Am I doing anything obviously wrong or stupid?
My only other thought so far was this:
Replace the group's id, parent and name fields with a unique "path" String which specifies the whole ancestry of a group, e.g.:
/rootGroup
/rootGroup/aChild
/rootGroup/aChild/aGrandChild
The join table between Groups and Items would then contain group_path and item_id.
This immediately solves the two issues I was suffering previously:
1. The entire group hierarchy can be fetched from the database in a single query and reconstructed in-memory.
2. To retrieve all Items in a group or its descendants, we can select from group_item where group_path='N' or group_path like 'N/%'
However, this seems to defeat the point of using Hibernate. All thoughts welcome!