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)
}
}
Tree Level (owningTree parentTree) mappedBy, .