在Primefaces 4和5中面临关键性能问题



我正在做一个处理大量数据集的项目。我正在使用Primefaces 4 &5,春季和休眠。我必须显示一个非常庞大的数据集,如最小3000行与100列的各种功能,如排序,过滤,行扩展等。我的问题是,我的应用程序花了8到10分钟来显示整个页面,其他功能(排序、过滤)也花了很多时间。我的委托人一点都不高兴。然而,我可以使用分页,但我的客户端不需要分页。所以我决定使用livescroll,但不幸的是,我未能实现livescroll与lazyload或没有lazyload,因为有关于livescroll的bug在PF。我也张贴了这个问题在这里早些时候,但没有找到解决方案。

这个性能问题非常关键,对我来说是一个障碍。要显示3000行100列,要加载的页面大小约为10MB。我已经计算了JSF的各种生命周期所消耗的时间,使用Phase-listener我发现它的浏览器正在花费时间来解析JSF呈现的响应。为了完成所有阶段,我的应用程序只花了25秒。至少我想提高我的项目的性能。请分享任何有助于克服这个问题的想法、建议和任何事情

注意: getter和setter中没有数据库操作,也没有复杂的业务逻辑。

更新:这是我的数据表,没有lazyload:

<p:dataTable 
                style="width:100%"
                id="cdTable"
                selection="#{controller.selectedArray}"
                resizableColumns="true" 
                draggableColumns="true" 
                var="cd"
                value="#{controller.cdDataModel}"
                editable="true" 
                editMode="cell"
                selectionMode="multiple"
                rowSelectMode="add"
                scrollable="true"
                scrollHeight="650"
                rowKey="#{cd.id}"
                rowIndexVar="rowIndex"
                styleClass="screenScrollStyle"
                liveScroll="true"
                scrollRows="50"
                filterEvent="enter"
                widgetVar="dt4"
                >

这里一切都在工作,除了过滤。一旦我过滤,然后第一页显示,但无法排序或实时滚动的数据表。注意,我已经在 prifaces5 中测试过了。

2号试验证实

与lazyload相同的数据表1)当我添加rows="100"时,会发生滚动,但行编辑,行扩展但过滤器&排序工作。2)当我删除 livescroll工作与行编辑,行扩展等,但过滤器&排序不起作用

我的LazyLoadModel如下

public class MyDataModel extends LazyDataModel<YData> 
         {
    @Override
    public List<YData> load(int first, int pageSize,
            List<SortMeta> multiSortMeta, Map<String, Object> filters) {
        System.out.println("multisort wala load");
        return super.load(first, pageSize, multiSortMeta, filters);
    }

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private List<YData> datasource;
    public YieldRecBondDataModel() {
    }
    public YieldRecBondDataModel(List<YData> datasource) {
         this.datasource = datasource;
    }


    @Override
    public YData getRowData(String rowKey) {
        // In a real app, a more efficient way like a query by rowKey should be
        // implemented to deal with huge data
    //  List<YData> yList = (List<YData>) getWrappedData();
        for (YData y : datasource) 
        {
            System.out.println("datasource :"+datasource.size());
            if(y.getId()!=null)
            {
            if (y.getId()==(new Long(rowKey)))
            {
                return y;
            }
            }
        }
        return null;
    }
    @Override
    public Object getRowKey(YData y) {
        return y.getId();
    }



      @Override
        public void setRowIndex(int rowIndex) {
            /*
             * The following is in ancestor (LazyDataModel):
             * this.rowIndex = rowIndex == -1 ? rowIndex : (rowIndex % pageSize);
             */
            if (rowIndex == -1 || getPageSize() == 0) {
                super.setRowIndex(-1);
            }
            else
                super.setRowIndex(rowIndex % getPageSize());
        }

      @Override
        public List<YData> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String,Object> filters) {
            List<YData> data = new ArrayList<YData>();
            System.out.println("sort order : "+sortOrder);
            //filter
            for(YData yInfo : datasource) {
                boolean match = true;
                for(Iterator<String> it = filters.keySet().iterator(); it.hasNext();) {
                    try {
                        String filterProperty = it.next();
                        String filterValue = String.valueOf(filters.get(filterProperty));
                        Field yField = yInfo.getClass().getDeclaredField(filterProperty);
                        yField.setAccessible(true);
                        String fieldValue = String.valueOf(yField.get(yInfo));
                        if(filterValue == null || fieldValue.startsWith(filterValue)) {
                            match = true;
                        }
                        else {
                            match = false;
                            break;
                        }
                    } catch(Exception e) {
                        e.printStackTrace();
                        match = false;
                    }
                }
                if(match) {
                    data.add(yInfo);
                }
            }
            //sort
            if(sortField != null) {
                Collections.sort(data, new LazySorter(sortField, sortOrder));
            }
            int dataSize = data.size();
            this.setRowCount(dataSize);
            //paginate
            if(dataSize > pageSize) {
                try {
                    List<YData> subList = data.subList(first, first + pageSize);
                    return subList;
                }
                catch(IndexOutOfBoundsException e) {
                    return data.subList(first, first + (dataSize % pageSize));
                }
            }
            else
                return data;
        }
    @Override
    public int getRowCount() {
        // TODO Auto-generated method stub
        return super.getRowCount();
    }
    }

我对这些问题感到厌倦,并成为我的表演障碍。我也试过Primefaces 5

如果你的数据是从数据库加载的,我建议你做一个更好的LazyDataModel:

 public class ElementiLazyDataModel extends LazyDataModel<T> implements Serializable {
        private Service<T> abstractFacade;

        public ElementiLazyDataModel(Service<T> abstractFacade) {
            this.abstractFacade = abstractFacade;
        }
        public Service<T> getAbstractFacade() {
            return abstractFacade;
        }
        public void setAbstractFacade(Service<T> abstractFacade) {
            this.abstractFacade = abstractFacade;
        }
        @Override
        public List<T> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
            PaginatedResult<T> pr = abstractFacade.findRange(new int[]{first, first + pageSize}, sortField, sortOrder, filters);
            setRowCount(new Long(pr.getTotalItems()).intValue());
            return pr.getItems();
        }
    }

服务是注入到使用此模型的ManagedBean中的某种后端通信(如EJB)。

分页服务可能如下所示:

@Override
    public PaginatedResult<T> findRange(int[] range, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
        final Query query = getEntityManager().createQuery("select x from " + entityClass.getSimpleName() + " x")
                .setFirstResult(range[0]).setMaxResults(range[1] - range[0] + 1);
        // Add filter sort etc.
        final Query queryCount = getEntityManager().createQuery("select count(x) from " + entityClass.getSimpleName() + " x");
        // Add filter sort etc.
        Long rowCount = (Long) queryCount.getSingleResult();
        List<T> resultList = query.getResultList();
        return new PaginatedResult<T>(resultList, rowCount);
    }

请注意,你必须做分页查询(与jpa这样的orm做查询为您,但如果你不使用orm必须做分页查询,为oracle查看TOP-N查询,例如:http://oracle-base.com/articles/misc/top-n-queries.php)

记住你的返回对象必须包含总记录作为快速计数:

public class PaginatedResult<T> implements Serializable {
    private List<T> items;
    private long totalItems;
    public PaginatedResult() {
    }
    public PaginatedResult(List<T> items, long totalItems) {
        this.items = items;
        this.totalItems = totalItems;
    }
    public List<T> getItems() {
        return items;
    }
    public void setItems(List<T> items) {
        this.items = items;
    }
    public long getTotalItems() {
        return totalItems;
    }
    public void setTotalItems(long totalItems) {
        this.totalItems = totalItems;
    }
}

如果您的数据库表设置正确,那么所有这些都是有用的,请注意可能查询的执行计划并添加正确的索引。

希望给你一些提示来提高你的表现

最后,要记住你的最终用户,人的眼睛一次只能看到10-20条记录,所以一页里有上千条记录是没有用的。

您已经使用了默认的load实现,该实现用于Primefaces的演示。对于您从数据库加载数据的情况,这不是正确的实现。

load方法应该使用正确的查询,并考虑:

1)所使用的过滤器字段,例如:

String query = "select e from Entity e where lower(e.f1) like lower('" + filters.get(key) + "'%) and..., etc. for the other fields

2)使用的排序列,例如:

query.append("order by ").append(sortField).append(" ").append(SortOrder.ASCENDING.name() ? "" : sortOrder.substring(0, 4)),..., etc. for the other columns.

3)带有1)的查询的总计数。例子:

Long totalCount = (Long) entityManager.createQuery("select count(*) from Entity e where lower(e.f1) like lower('filterKey1%') and lower(e.f2) like lower('filterKey2%') ...").getSingleResult();

相关内容

  • 没有找到相关文章

最新更新