我在Grails中有一个与此类似的树结构:
class TreeNode {
String name
// more properties
List children = []
static hasMany = [children: TreeNode]
static belongsTo = [parent: TreeNode, root: TreeNode]
static mappedBy = [children:'parent']
static constraints = {
name(blank: false,maxsize: 100,)
parent(nullable:true)
root(nullable:false)
}
}
出于SQL性能原因,我需要每个记录直接引用其根节点。因此,'root'属性。
在添加“root”之前,节点已正确保存。也就是说,我可以在根节点上调用save(),并在所有节点中正确分配parent_id字段(除了root本身,其父ID保持为null)。
我试图通过向上走树直到找到父对象来在beforeInsert()中指定'root'。
def beforeInsert = {
def node = this
while (node.parent) {
node = node.parent
}
root = node
}
我经常收到堆栈溢出异常。
at org.codehaus.groovy.grails.orm.hibernate.validation.HibernateDomainClassValidator.cascadeValidationToOne(HibernateDomainClassValidator.java:116)
at org.codehaus.groovy.grails.validation.GrailsDomainClassValidator.cascadeToAssociativeProperty(GrailsDomainClassValidator.java:142)
当没有发生堆栈溢出异常时,每个“root”引用自身而不是最终根。 beforeInsert()不会爬树。
你能帮我解决这个问题吗? GORM是否尝试先深度保存我的树结构?如果是这样,如果父级尚未保留,它如何保存父ID?最重要的是,如何通过在根节点上调用save()来保存树,这样“root”是否正确设置而不必在save()之后立即更新所有节点?
答案 0 :(得分:1)
好吧,通过始终保持根节点更新,你会发现自己不断迭代树,这是你想要的通过保持对根节点的引用。但是如果你真的想保留一个根节点,你可以试试这个结构:
Class TreeNode {
String name
TreeNode parent
TreeNode root
static hasMany = [children: TreeNode]
TreeNode getRoot(){
//of course, if parent is null, it means, you're already in the root node.
if(parent){
return parent.getRoot()
}else{
return this
}
}
}
因此,您不必担心每次都明确更新根节点。只需设置子节点即可完成。 Root可以在dinamically中找到(但同样,你将以某种方式在树中迭代)。另外,我认为这是一个更清晰的映射;)
编辑:我把另一个引用(到root)。因此,您可以选择是否使用getRoot方法将其设置为dinamically。在任何情况下,每个节点都将具有对根的引用。
答案 1 :(得分:0)
由于root
引用this
,所以必须发生这种情况,并且验证会进入无限循环。尝试更改为:
if (!parent) {
root = this
return
}
def node = parent
while (node.parent) {
node = node.parent
}
root = node
如果一个节点引用自身而不是根目录 - 这意味着你没有分配parent
(错误地)。
GORM从您调用save()
的实例开始,并首先保存其所有子节点(belongsTo
实例)。因此,树应保存在ultimateRoot.save()
。