Tree structure in GORM (grails)

2019-02-15 09:41发布

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

3条回答
霸刀☆藐视天下
2楼-- · 2019-02-15 10:01

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

查看更多
何必那么认真
3楼-- · 2019-02-15 10:04

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.

查看更多
放我归山
4楼-- · 2019-02-15 10:23

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.

查看更多
登录 后发表回答