1

我正在尝试在 PrimeFaces 库中使用 Tree 组件,但由于数据量很大,我必须懒惰地加载它。我按照教程进行操作(http://blog.disy.net/primefaces-lazy-tree/),但加载需要 15 秒(而不是 23 秒)。我在本教程或我的代码中遗漏了什么吗?我不能这样,因为 15 秒对用户来说太长了。

代码:

public interface TreePodeFindWithParent {

List<VArboParObjectifsParents> findActiviteWithParent(Integer parentId); 

}

延迟加载类中的方法

public class PodeLazyTreeNode extends DefaultTreeNode
{
...
...
public PodeLazyTreeNode(VArboParObjectifsParents data, TreePodeService service)
{
super(vArboParObjectifsParents.class.getSimpleName(), data, null);
this.service = service;
}

...

private void ensureChildrenFetched() 
{
    if (!childrenFetched)
    {
        childrenFetched = true;
        if ((VArboParObjectifsParents)getData() != null)
        {
            Integer parentId = ((VArboParObjectifsParents)getData()).getIdRoot();

            List<PodeLazyTreeNode> childNodes = service.findActiviteWithParent(parentId).stream().map(item 
                    -> new PodeLazyTreeNode(item, service)).collect(Collectors.toList());

            super.getChildren().addAll(childNodes);
        }

    }
}

服务中的方法

public class TreePodeService implements Serializable, TreePodeWithParent
{
...
...
@Inject
private PodeArboParObjectifsParentsDao podeArboObjParentDao;
...
@Override
public List<VArboParObjectifsParents> findActiviteWithParent(Integer parentId) {
    // TODO Auto-generated method stub
    return podeArboObjParentDao.findByIdParent(parentId);
}

DAO(使用 Apache DeltaSpike 的数据模块完成请求):

@Repository(forEntity=VArboParObjectifsParents.class)
public interface PodeArboParObjectifsParentsDao extends EntityRepository<VArboParObjectifsParents, Integer>
{
List<VArboParObjectifsParents> findByIdParent(Integer idParent);
List<VArboParObjetcifsParents> findByIdTypeActivite(Integer idType);
}

视图中方法的调用:

@PostConstruct
public void initView()
{
    initArbo();
}

public void initArbo()
{
    List<VArboParObjectifsParents> vArbos = treePodeService.getPodeArboObjParentDao().findByIdTypeActivite(1);
    this.root = new PodeLazyTreeNode(null, treePodeService);
    for (int i = 0; i < vArbos.size(); i++)
    {
        root.getChildren().add(new PodeLazyTreeNode(vArbos.get(i), treePodeService));
    }
}

用户界面:

<p:tree value="#{testView.root}" var="_node" >
                <p:treeNode type="VArboParObjectifsParents">
                    <h:outputText value="#{_node}"/>
                </p:treeNode>

             </p:tree> 
4

1 回答 1

1

问题的原因

来自 PrimeFaces展示

树有两种模式,在客户端模式下,所有节点都在客户端可用,而在 ajax 模式下,只有扩展节点可用。

这意味着在树变得可见之前,树的所有节点都被加载到浏览器中。您拥有的节点越多,加载整个树所需的服务调用就越多。您链接的页面上还有一个提示:

n+1 问题

到目前为止,我们的实现非常简单。但是,它确实给 BackendService 带来了很大的压力。每当扩展节点时,都会为扩展节点的每个子节点调用 BackendService.findWithParent(...)。这称为 n+1 问题,因为您需要 n+1 次服务调用来提供扩展节点。


解决方案

dynamic="true"通过设置(并可选择始终重新加载子节点)使用树的 ajax 模式,并cache="false"通过在视图中调用侦听器来加载/删除子节点。

另外请不要通过构造函数指定服务。在我看来,您找到的教程非常糟糕。将服务注入后备 bean 并在那里加载节点数据。

例子:

小面 (UI)

<p:tree cache="false"
        dynamic="true"
        value="#{testView.root}" var="_node">

  <!--load/update nodes on demand-->
  <p:ajax event="expand"
          listener="#{testView.onNodeExpand}" />
  <p:ajax event="collapse"
          listener="#{testView.onNodeCollapse}" />

  <p:treeNode type="VArboParObjectifsParents">
    <h:outputText value="#{_node}"/>
  </p:treeNode>
</p:tree>

支持豆(视图)

@Named
@ViewScoped
public TestView implements Serializable {

    @PostConstruct
    public void initView()
    {
        initArbo();
    }


    public void initArbo()
    {
        List<VArboParObjectifsParents> vArbos = treePodeService.getPodeArboObjParentDao().findByIdTypeActivite(1);
        this.root = new PodeLazyTreeNode(null);
        for (int i = 0; i < vArbos.size(); i++)
        {
            PodeLazyTreeNode childNode = new PodeLazyTreeNode(vArbos.get(i), this.root);
            addDummyTreeNode(childNode);
        }
    }

    public void onNodeExpand(NodeExpandEvent event) {
        final TreeNode expandedTreeNode = event.getTreeNode();

        // load child data from service
        // ...
        if (/* child data available */) {
            // create child tree nodes with dummy tree node
            // ...
            expandedTreeNode.setExpanded(true);
        } else {
            // remove dummy tree nod to mark as leaf
            // ...
            expandedTreeNode.setExpanded(false);
        }
    }

    public void onNodeCollapse(NodeCollapseEvent event) {
        final TreeNode collapsedTreeNode = event.getTreeNode();

        // remove child nodes and add dummy tree node
        // ...

        collapsedTreeNode.setExpanded(false);
    }

    /**
     * Adds a dummy {@link TreeNode} to the specified parent node. The dummy is
     * used to prevent nodes with child nodes from being rendered as leaf nodes
     * until they are expanded. The dummy will be removed when the parent node
     * is expanded the first time.
     *
     * @param parentTreeNode
     */
    private void addDummyTreeNode(TreeNode parentTreeNode) {
        final TreeNode dummyNode = new DefaultTreeNode("Loading...",
                parentTreeNode);
    }

}
于 2018-09-28T17:25:59.440 回答