I'm trying to define a tree structure in GORM. Here is my model:
class Tree {
String name
Level rootLevel
static hasOne = [rootLevel: Level]
static hasMany = [levels: Level]
static mappedBy = [levels:"parentTree"]
}
class Level {
String name
Tree parentTree
Level parentLevel
Set<Level> subLevels
static belongsTo = [parentTree: Tree]
static hasMany = [subLevels: Level]
}
Insertion seems to work fine, but when I can't load a Tree with many levels and sublevels.
I guess I missed something in the relations:
- the Tree should have a reference to the rootLevel (and optionally to all sublevels)
- a Level should have a reference to its parent level, its sublevels and the global parent Tree
Could you point me out the right direction to get a tree structure like this ?
Thanks
I didn't like your tree structure, so i created my own :)
Class TreeNode {
String name
TreeNode parent
static hasMany = [children: TreeNode]
//returns the root node, and by extension, the entire tree!
TreeNode getRootNode(){
if(parent){
//if parent is not null then by definition this node is a child node of the tree.
return parent.getRootNode()
}else{
//if parent is null then by definition it is the root node.
return this
}
}
//you might not need this function, but ill add it as it is common in tree structures
boolean isLeaf(){
//determines if this node is a leaf node. a leaf is a node with zero childrens
return children.isEmpty()
}
}
As for ensuring that all treenodes is loaded, you can always use eager/non-lazy fetching for each treeNode both parent and children. Could however have a performance penalty if your tree structures are very large...
As for eager/lazy fetching. take a look here: Using lazy property fetching in Grails / Gorm
The problem is that levels are filled with the parent Tree id, but when you load the Tree, you have the following error "More than one row with the given identifier was found" for the Level class.
Your problem seems to be that you have more than a single root node for each tree. This is an unusual approach. To make it work, you must replace Level rootLevel
in the entity Tree
with Set<Level> roots
.
I ended up with this solution (thanks to a friend):
class Tree {
String name
Level rootLevel
static hasMany = [levels: Level]
static mappedBy = [rootLevel: "parentTree", levels: "owningTree"]
static constraints = {rootLevel(nullable: true)}
}
and
class Level {
String name
Tree parentTree
Tree owningTree
Level parentLevel
Set<Level> subLevels
static belongsTo = [owningTree: Tree, parentLevel: Level]
static hasMany = [subLevels: Level]
static mappedBy = [parentTree: "rootLevel", owningTree: "levels", subLevels: "parentLevel"]
static constraints = {
parentTree(nullable: true)
parentLevel(nullable: true)
}
}
I was missing the two relations between Tree and Level (owningTree and parentTree) and some mappedBy configuration to help hibernate.