Primefaces 具有DefaultTreeNode子类的p:treenode的奇怪行为

Primefaces 具有DefaultTreeNode子类的p:treenode的奇怪行为,primefaces,treenode,Primefaces,Treenode,我尝试在PrimeFaces4.0的动态模式下与持久化API一起使用p:tree。 我有@Entity类(Nomen),它已经有了引用自身的元素。我尝试了两种变体:在Nomen类接口TreeNode中实现,并使其成为DefaultTreeNode的子类。最后一个变体扩展了DefaultTreeNode。行为也是如此 @Entity @Table(name = "Nomen") public class Nomen extends DefaultTreeNode implements Serial

我尝试在PrimeFaces4.0的动态模式下与持久化API一起使用p:tree。 我有@Entity类(Nomen),它已经有了引用自身的元素。我尝试了两种变体:在Nomen类接口TreeNode中实现,并使其成为DefaultTreeNode的子类。最后一个变体扩展了DefaultTreeNode。行为也是如此

@Entity
@Table(name = "Nomen")
public class Nomen extends DefaultTreeNode implements Serializable {
@Id
private Integer id;
...
@Column(name = "Name")
private String name;
...
@JoinColumn(name = "Self_Id", referencedColumnName = "Id")
@ManyToOne
private Nomen parent;

@OneToMany(mappedBy = "parent", fetch=FetchType.EAGER)
private List<Nomen> nomenCollection;
...
public Nomen() {
    super();
}
...
@Override
public String getType() {
    if (nomenCollection == null) return "NomenLeaf";
    return "NomenGroup";
}

@Override
public Nomen getData() {
    Logger.getLogger(this.getClass().getName()).log(Level.INFO, "getData for {0}", new Object[] {this});
    return this;
}


@Override
public Nomen getParent() {
    return this.parent;
}
@Override
public void setParent(TreeNode tn) {
    if (!(tn instanceof Nomen)) {
        return;
    }
    try {
        parent.removeChild(this);
    }
    catch (NullPointerException e) { }// Nothing to do
    if (tn != null) {
        ((Nomen)tn).addChild(this);
    }
    parent = (Nomen)tn;
}

@Override
public int getChildCount() {
    if (nomenCollection == null) return 0;
    return nomenCollection.size();
}

@Override
public boolean isLeaf() {
    return nomenCollection == null;
}

@Override
public String getRowKey() {
    Logger.getLogger(this.getClass().getName()).log(Level.INFO, "getRowKey: this: {0} id: {1}", new Object[] {this, Integer.toString(getId())} );
    return Integer.toString(getId());
}
@Override
public List<TreeNode> getChildren() {
//       return new ArrayList<TreeNode> (nomenCollection);
//       return (List<TreeNode>)(List<? extends TreeNode>)nomenCollection;
    ArrayList<TreeNode> r = new ArrayList<> ();
    for (Nomen n : nomenCollection) {
        r.add(n);
    }
    Logger.getLogger(this.getClass().getName()).log(Level.INFO, "getChildren for {0}: List: {1} ", new Object[] {this, r});
    return r;
}
public List<Nomen> getNomenCollection() {
    return nomenCollection;
}
public void setNomenCollection (List<Nomen> n) {
    nomenCollection = n;
}
}
电话的顺序似乎很奇怪

  • 日志的第5行没有调用getData(应先调用getData,然后再调用getRowKey,如第9,11行所示)

  • 根目录的getChildren调用过多(预期为getChildren调用,然后使用后续getData调用遍历列表)

  • 我不明白怎么回事。跟踪getChildren调用可以提供更深层的正确树结构。集合被跟踪以实例化。(添加FetchType.EAGER未更改任何内容)。我计划使用带有拖放功能的p:tree组件来实现层次编辑器,但现在到此为止。我不希望在DefaultTreeNode中包含Nomen作为数据对象,然后复制实体类本身已经实现的自然树结构。然后同步两个父级更改;-)


    有人能帮忙吗?

    这个问题可以通过注释掉下面的代码来解决

    /*
    @Override
    public String getRowKey() {
        Logger.getLogger(this.getClass().getName()).log(Level.INFO, "getRowKey: this: {0} id: {1}", new Object[] {this, Integer.toString(getId())} );
        return Integer.toString(getId());
    }
    */
    
    我当然不知道p:tree是如何使用getRowKey的。很可能,它必须以某种方式对应于p:tree的datakey属性。我还没有深入研究它们应该如何协同工作的文档。但调用序列中的所有混乱都是由重写getRowKey()引起的


    谢谢大家的阅读

    挖掘DefaultTreeNode的源代码发现了一些微妙的特性,这些特性在可用的文档中没有得到很好的反映

    首先,我指的是特殊(我认为一般)用例:我有@Entity类,它已经包含引用自身的元素。我尝试为该类实现层次编辑器。几年前论坛上有人建议使用DefaultTreeNode子类,我决定也这样做。一些基本的东西在文档中被遗漏了,我不得不研究p的真实生物:树和树节点。在这里,如果有人需要的话,我试着描述这些细微的特征

  • 在我的例子中(PF4.0、JSF2.2、Netbeans8.0、Glassfish4.0),为p:tree添加可拖动属性是不够的。我必须补充一点 p:thattree可拖动,p:Dropable可拖放

    <p:tree id="ntree" value="#{treeController.root}" var="nod" draggable="true" droppable="true" dynamic="true">
        <p:ajax event="dragdrop" listener="#{treeController.onDragDrop}" update="@this">
    ...
    </p:tree>
    <p:draggable for="ntree" handle=".ui-treenode-label, .ui-treenode-icon"/>
    <p:droppable for="ntree"/>
    
    
    ...
    
  • 线

    <p:ajax event="dragdrop" ... 
    
    
    
    <p:tree id="ntree" value="#{treeController.root}" var="nod" draggable="true" droppable="true" dynamic="true">
        <p:ajax event="dragdrop" listener="#{treeController.onDragDrop}" update="@this">
    ...
    </p:tree>
    <p:draggable for="ntree" handle=".ui-treenode-label, .ui-treenode-icon"/>
    <p:droppable for="ntree"/>
    
    <p:ajax event="dragdrop" ... 
    
    public abstract class AbstractTreeNode<T> extends DefaultTreeNode {
    
        @Override
        public abstract String getType();
    
        @Override
        public abstract AbstractTreeNode<T> getParent();
    
        @Override
        public abstract void setParent(TreeNode tn);
    
        @Override
        public abstract List<TreeNode> getChildren();
    
        public AbstractTreeNode() { super(); }
    
        @Override
        public T getData() {
            return (T)this;
        }
    
        @Override
        public int getChildCount() {
            if (getChildren() == null) return 0;
            return getChildren().size();
        }
    
        @Override
        public boolean isLeaf() {
            return (getChildren() == null) || getChildren().isEmpty();
        }
    
        @Override
        public final String getRowKey() {
            String r;
            if (getParent() == null) r = "";
            else if (getParent().getParent() == null)
                r = ""+getParent().getChildren().indexOf(this);
            else {
                r = getParent().getRowKey() + "_" + getParent().getChildren().indexOf(this);
            }
            return r;
        }
    }
    
    @Entity
    public class SomeTable extends AbstractTreeNode<SomeTable> implements Serializable {
    ...