JSF 大数据分页和排序研究

本文介绍了一种在JSF中实现大数据表格高效分页及排序的方法,通过自定义组件并配合特殊数据模型,使得即使面对大量数据也能保持良好的性能表现。
 
Today we are going to talk about sorting and paging very large tables using JSF standard components. To tell the truth I had no satisfying solution to this problem since I have started dealing with JSF, because neither MyFaces, nor Tomahawk offer good components for displaying large tables with sorting an paging. All the table components are fit only for small dataset sizes, which are stored in memory. If the dataset is too large to hold it in RAM, apache wiki offers a simple recipe.

This sample is very good, and demostrates the general approach to dealing with large datatables, but it is no good when you need to sort the data. If you attempt to turn on sorting within the table component, your database will be literally flooded with requests. To sort the data, all the records need to be fetched, though it is done in portions and does not consume too much memory, it awfully spams the database. I did not want to write the whole component from scratch, so I have found a temporary workaround - added sorting and paging logic to a managed bean and added commandLinks to go to next/previos page and to do sorting. What I lacked however, were the paging links 1,2,3,4,5.... which I found too difficult to incorporate into existing component.

Well everything good ends sometime and our customer requested paging to be "like at Google". So I had to turn back to the and had to study JSF once again and write my own component. This is what I've got in the end.

Having read the code of and , I realized that writing the whole component from scratch would be the worst nightmare (given the fact that I hate JSF). However I have found a way to extend the existing component and make it act as I want. In the end it appeared that the amount of code was not that great. The whole project for the component can be downloaded from here.

I took the code from apache wiki as the basis, tweaking it along the way to get the best performance.

This is the code for results page:

package com.anydoby.jsfpager;

import java.io.Serializable;
import java.util.List;

/**
 * A simple class that represents a "page" of data out of a longer set, ie
 * a list of objects together with info to indicate the starting row and
 * the full size of the dataset. EJBs can return instances of this type
 * when returning subsets of available data.
 */
public class DataPage<T> implements Serializable {

	private static final long serialVersionUID = -5211047724709570419L;
	private int startRow;
	private List<T> data;

	/**
	 * Create an object representing a sublist of a dataset.
	 * 
	 * @param startRow is the index within the complete dataset
	 * of the first element in the data list.
	 * 
	 * @param data is a list of consecutive objects from the
	 * dataset.
	 */
	public DataPage(int startRow, List<T> data) {
		this.startRow = startRow;
		this.data = data;
	}

	/**
	 * Return the offset within the full dataset of the first
	 * element in the list held by this object.
	 */
	public int getStartRow() {
		return startRow;
	}

	/**
	 * Return the list of objects held by this object, which
	 * is a continuous subset of the full dataset.
	 */
	public List<T> getData() {
		return data;
	}
}

This DataModel ensures paging:

package com.anydoby.jsfpager;

import java.io.Serializable;

import javax.faces.model.DataModel;

/**
 * A special type of JSF DataModel to allow a datatable and datascroller
 * to page through a large set of data without having to hold the entire
 * set of data in memory at once.
 * <p>
 * Any time a managed bean wants to avoid holding an entire dataset,
 * the managed bean should declare an inner class which extends this
 * class and implements the fetchData method. This method is called
 * as needed when the table requires data that isn't available in the
 * current data page held by this object.
 * <p>
 * This does require the managed bean (and in general the business
 * method that the managed bean uses) to provide the data wrapped in
 * a DataPage object that provides info on the full size of the dataset.
 */
public abstract class PagedListDataModel<T> extends DataModel implements Serializable {

	int pageSize;
	int rowIndex = -1;
	DataPage<T> page;
	private int datasetSize = -1;
    private int lastStart;
    private int lastSize;

	/*
	 * Create a datamodel that pages through the data showing the specified
	 * number of rows on each page.
	 */
	public PagedListDataModel(int pageSize) {
		this.pageSize = pageSize;
	}

	/**
	 * Not used in this class; data is fetched via a callback to the
	 * fetchData method rather than by explicitly assigning a list.
	 */
	@Override
	public void setWrappedData(Object o) {
		throw new UnsupportedOperationException("setWrappedData");
	}

	@Override
	public int getRowIndex() {
		return rowIndex;
	}

	/**
	 * Specify what the "current row" within the dataset is. Note that
	 * the UIData component will repeatedly call this method followed
	 * by getRowData to obtain the objects to render in the table.
	 */
	@Override
	public void setRowIndex(int index) {
		rowIndex = index;
	}

	/**
	 * Return the total number of rows of data available (not just the
	 * number of rows in the current page!).
	 */
	@Override
	public int getRowCount() {
		if (datasetSize == -1) {
			datasetSize = getDatasetSize(); 
		}
		return datasetSize;
	}

    /**
     * Return the total number of rows of data available (not just the
     * number of rows in the current page!).
     */
	protected abstract int getDatasetSize();

	/**
	 * Return a DataPage object; if one is not currently available then
	 * fetch one. Note that this doesn't ensure that the datapage
	 * returned includes the current rowIndex row; see getRowData.
	 */
	private DataPage<T> getPage() {
		if (page != null) {
			return page;
        }

		int rowIndex = getRowIndex();
		int startRow = rowIndex;
		if (rowIndex == -1) {
			// even when no row is selected, we still need a page
			// object so that we know the amount of data available.
			startRow = 0;
		}

		// invoke method on enclosing class
		page = fetchPageInternal(startRow, pageSize);
		return page;
	}

	/**
	 * Return the object corresponding to the current rowIndex.
	 * If the DataPage object currently cached doesn't include that
	 * index then fetchPage is called to retrieve the appropriate page.
	 */
	@Override
	public Object getRowData() {
		if (rowIndex < 0) {
			throw new IllegalArgumentException(
					"Invalid rowIndex for PagedListDataModel; not within page");
		}

		// ensure page exists; if rowIndex is beyond dataset size, then 
		// we should still get back a DataPage object with the dataset size
		// in it...
		if (page == null) {
			page = fetchPageInternal(rowIndex, pageSize);
		}

		// Check if rowIndex is equal to startRow,
		// useful for dynamic sorting on pages

		if (rowIndex == page.getStartRow()) {
			page = fetchPageInternal(rowIndex, pageSize);
		}

		int datasetSize = getRowCount();
		int startRow = page.getStartRow();
		int nRows = page.getData().size();
		int endRow = startRow + nRows;

		if (rowIndex >= datasetSize) {
			throw new IllegalArgumentException("Invalid rowIndex");
		}

		if (rowIndex < startRow) {
			page = fetchPageInternal(rowIndex, pageSize);
			startRow = page.getStartRow();
		} else if (rowIndex >= endRow) {
			page = fetchPageInternal(rowIndex, pageSize);
			startRow = page.getStartRow();
		}

		return page.getData().get(rowIndex - startRow);
	}

	private DataPage<T> fetchPageInternal(int start, int size) {
        if (lastStart != start || lastSize != size || page == null) {
            page = fetchPage(start, size);
            lastSize = size;
            lastStart = start;
        }
        return page;
    }

    @Override
	public Object getWrappedData() {
		return getPage().getData();
	}

	/**
	 * Return true if the rowIndex value is currently set to a
	 * value that matches some element in the dataset. Note that
	 * it may match a row that is not in the currently cached 
	 * DataPage; if so then when getRowData is called the
	 * required DataPage will be fetched by calling fetchData.
	 */
	@Override
	public boolean isRowAvailable() {
        int rowCount = getRowCount();
        if (rowCount < 1) {
            return false;
        }
		int rowIndex = getRowIndex();
		if (rowIndex < 0) {
			return false;
		} else {
            return !(rowIndex >= getRowCount());
        }
	}

	/**
	 * Method which must be implemented in cooperation with the
	 * managed bean class to fetch data on demand.
	 */
	public abstract DataPage<T> fetchPage(int startRow, int pageSize);
}

The rest of the code allows us to use a custom component and sort the data manually:

package com.anydoby.jsfpager;

/**
 * This is the base class for datamodels returned by the beans, which want their
 * contents be sortable and pageable in the most efficient way. Subclasses can get
 * the sortColumn and ascending properties during resultset retrieval.
 * 
 * @author szolotaryov
 * 
 * Oct 10, 2007
 */
public abstract class SortablePagedDataModel<T> extends PagedListDataModel<T> {

	private static final long serialVersionUID = -777764054824546815L;
	private String sortColumn;
	private boolean sortAscending;

	public SortablePagedDataModel(int pageSize) {
		super(pageSize);
	}

	public final String getSortColumn() {
		return sortColumn;
	}

	public final void setSortColumn(String sortColumn) {
		this.sortColumn = sortColumn;
	}

	public final boolean isSortAscending() {
		return sortAscending;
	}

	public final void setSortAscending(boolean sortAscending) {
		this.sortAscending = sortAscending;
	}

}

Nothing too complicated heh. This is the datamodel which we will use. You will have to extend it and implement: fetchPage(int startRow, int pageSize) и int getDatasetSize(), which fetch the data page and total amount of records, accordingly.

As I have mentioned already, I needed to write a "custom" component. Actually not much to code:

package com.anydoby.jsfpager;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.faces.el.ValueBinding;
import javax.faces.model.DataModel;

import org.apache.myfaces.component.html.ext.HtmlDataTable;


public class SortableDataTable extends HtmlDataTable {

    public final static String COMPONENT_TYPE = "com.anydoby.jsfpager.SortableDataTable";
    private Boolean sortable;

    @SuppressWarnings("unchecked")
    @Override
    protected DataModel createDataModel() {
        DataModel model;
        boolean sortable = isSortableModel();
        if (!sortable) {
            model = super.createDataModel();
        } else {
            Object value = getValue();
            if (value instanceof SortablePagedDataModel) {
                SortablePagedDataModel sortableModel = (SortablePagedDataModel) value;

                String sortColumn = getSortProperty();
                boolean sortAscending = isSortAscending();
                sortableModel.setSortAscending(sortAscending);
                sortableModel.setSortColumn(sortColumn);

                model = sortableModel;
            } else {
                throw new IllegalArgumentException(
                        "If table is sortable you should provide an implementation of SortablePageDataModel as value");
            }
        }

        return model;
    }

    public void setSortableModel(boolean sortable) {
        this.sortable = sortable ? Boolean.TRUE : Boolean.FALSE;
    }

    @SuppressWarnings("deprecation")
    public boolean isSortableModel() {
        if (this.sortable != null)
            return this.sortable.booleanValue();
        ValueBinding vb = getValueBinding("sortableModel");
        Boolean v = vb != null ? (Boolean) vb.getValue(getFacesContext()) : null;
        return v != null ? v.booleanValue() : false;
    }

    @Override
    public Object saveState(FacesContext context) {
        Object[] objs = (Object[]) super.saveState(context);
        List<Object> list = new ArrayList<Object>(Arrays.asList(objs));
        list.add(sortable);
        objs = list.toArray(new Object[list.size()]);
        return objs;
    }
    
    @Override
    public void restoreState(FacesContext context, Object state) {
        super.restoreState(context, state);
        Object[] objs = (Object[])state;
        sortable = (Boolean) objs[objs.length - 1];
    }


}

The table without the sortableModel attribute will act the same as original . If sortableModel=true and the model is SortablePagedDataModel, then the sorting state is communicated to it.

And finally, the tag class which binds JSP and your component:

package com.anydoby.jsfpager;

import org.apache.myfaces.taglib.html.ext.HtmlDataTableTag;

public class SortableDataTableTag extends HtmlDataTableTag {

	private String sortableModel;

	@Override
	public String getComponentType() {
		return SortableDataTable.COMPONENT_TYPE;
	}

	@Override
	public void release() {
		super.release();
		sortableModel = null;
	}

	@Override
	protected void setProperties(UIComponent component) {
		super.setProperties(component);
		setBooleanProperty(component, "sortableModel", sortableModel);
	}

	public String getSortableModel() {
		return sortableModel;
	}

	public void setSortableModel(String sortableModel) {
		this.sortableModel = sortableModel;
	}

}

The attachment to this article contains the complete code for the project (except for the tomahawk.jar and myfaces), tld, faces-config.xml and jsp with usage example. You can launch build.xml, first put the required jars into the lib folder and in the dist you will get a jar with component. Place it to your WEB-INF/lib, add to the classpath of your IDE and it can be used.

If you do not want to compile, here is the compiled jar file which I use.

If preserveDataModel="false", then fetchPage will be invoked twice the first time with start=0, and the second time with current start value, therefore I would recomment making all your model classes Serializable and setting preserveDataModel="true".

 

内容概要:本文系统研究了基于豪猪优化算法(CPO)的多无人机协同集群在三维空间中的避障路径规划问题,聚焦于实现以最低成本为目标的航迹优化,综合考虑路径长度、飞行高度、威胁规避及转弯角度等多个关键因素。通过构建精细化的三维环境模型与多无人机协同机制,采用Matlab平台实现CPO算法的仿真与验证,充分展示了该算法在复杂动态障碍环境下的高效搜索能力与全局优化性能。研究不仅涵盖了路径规划的数学建模与目标函数设计,还深入探讨了算法的收敛特性与鲁棒性,为智能群体系统在实际场景中的应用提供了理论依据与技术支撑。; 适合人群:具备一定编程基础优化算法背景,从事无人机系统控制、智能路径规划、群体协同、人工智能与自动化等相关领域的科研人员、高校研究生及工程技术人员。; 使用场景及目标:①应用于多无人机协同执行侦察、灾害监测、应急救援、区域巡检等复杂任务中的自主路径规划;②为智能优化算法在三维动态环境下的路径决策问题提供可复现的技术范例;③支持研究人员对CPO算法与其他主流群智能算法(如PSO、GWO、WOA等)进行性能对比与改进研究,推动路径规划技术的发展。; 阅读建议:建议结合提供的Matlab代码进行实践操作,重点理解目标函数的多维度建模方式与CPO算法的迭代优化流程,可通过调整环境参数与约束条件进行仿真实验,对比不同算法在相同场景下的路径质量与收敛速度,从而深入掌握其优势与适用边界。
内容概要:本文围绕电动汽车参与电力系统运行备用的能力评估展开深入研究,利用Matlab代码实现对电动汽车集群提供运行备用服务的建模与仿真分析。研究重点在于量化电动汽车作为分布式灵活资源参与电网辅助服务的潜力,通过构建精细化的数学模型,分析其可调功率容量、响应速度、时空分布特性及聚合能力,并采用多面体聚合、内近似模型与闵可夫斯基等先进方法精确刻画其可调度能力边界。研究进一步结合大规模电动汽车接入场景,探讨其在多时间尺度调度框架下参与调峰、调频等辅助服务的优化策略,评估其对提升高比例可再生能源电网灵活性与稳定性的贡献,最终通过仿真验证所提模型与方法的有效性与实用性。; 适合人群:具备电力系统分析、智能电网、新能源汽车或优化调度等相关专业背景,熟悉Matlab/Simulink仿真工具,从事科研、工程应用的高校研究生、科研人员及电力行业工程师。; 使用场景及目标:①精确评估大规模电动汽车集群在不同约束条件下可提供的运行备用容量;②研究电动汽车在日前、日内及实时调度中的动态响应能力与优化调度策略;③为高渗透率新能源电力系统提供基于移动储能的灵活性资源解决方案,支撑电网安全经济运行。; 阅读建议:建议结合Matlab代码与技术文档同步学习,重点关注多面体聚合建模、能力边界计算及优化调度算法的设计与实现,可进一步拓展至V2G(车辆到电网)、需求响应等互动场景进行二次开发与应用验证。
打开链接下载源码: https://pan.quark.cn/s/a4b39357ea24 OpenCV(开源计算机视觉库)中的DNN(Deep Neural Network)模块是一种功能强大的工具,其目的是用于深度学习模型的操作。该模块使得开发人员能够在OpenCV环境中直接运用已经训练好的深度学习网络,以执行图像识别、目标检测、图像分割等多种功能。DNN模块能够兼容多种深度学习框架的模型,包括TensorFlow、Caffe、ONNX等。 一、DNN模块概述 OpenCV的DNN模块是为了简化深度学习模型的集成过程而专门设计的,它允许开发人员加载预先训练好的神经网络模型,并在图像数据上执行前向传播操作。借助这个模块,用户可以选用GPU或者CPU来提升计算效率,从而构建出高效的应用程序。 二、目标检测案例 在OpenCV的DNN模块中,目标检测是一个常见的应用情形。例如,可以选用SSD(Single Shot Multibox Detector)、YOLO(You Only Look Once)或者 Faster R-CNN 等模型进行实时的目标检测。这些模型能够识别并定位图像中的多个对象,并返回每个对象的类别边界框坐标。 三、模型转换:PB到PBTXT 在OpenCV中运用TensorFlow模型时,通常需要处理的是`.pb`格式的模型文件,这是TensorFlow的二进制模型文件格式。然而,为了能够读取模型的结构信息,我们需要`.pbtxt`格式的文本文件。转换过程涉及解析`.pb`文件并将其结构信息导出为`.pbtxt`格式,这样做可以让人清晰地了解网络层参数的配置。在OpenCV中,可以使用`tf.train.write_graph()`函数将.pb...
内容概要:本文围绕虚拟同步发电机(VSG)接入弱电网的序阻抗建模与稳定性分析开展研究,基于Matlab/Simulink平台搭建详细的仿真模型,系统复现并验证相关理论方法。研究重点包括VSG在弱电网条件下的正负序阻抗特性建模、基于小信号分析的扫频法建模流程、系统阻抗交互特性及潜在的失稳机理分析。通过具体仿真案例,深入探讨了VSG控制参数对系统稳定性的影响,旨在为新能源并网系统的稳定运行提供理论依据与技术支撑。该内容属于电力电子与电力系统稳定性交叉领域的前沿课题,具有重要的学术价值与工程应用前景。; 适合人群:具备电力系统分析、电力电子变换器控制等基础知识,熟悉Matlab/Simulink仿真环境,从事新能源并网、微电网控制、电力系统稳定性研究研究生、科研人员及工程师;有志于复现高水平期刊论文中阻抗建模与稳定性分析方法的技术开发者。; 使用场景及目标:① 掌握虚拟同步发电机在弱电网中的序阻抗建模理论与实现方法;② 理解并实践基于扫频法的小信号稳定性分析全过程;③ 应用于构网型变流器、虚拟同步机等先进并网技术的稳定性研究与仿真验证。; 阅读建议:建议结合所提供的Simulink仿真模型与技术资料,按照文档结构循序渐进地学习,重点关注建模原理、仿真参数设置与结果分析过程,同时参考链接中的完整资源进行代码调试与深入探究。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值