【第二版】RichFaces中使用datatable和datascroller进行分页(使用数据库分页,改良版)(含源码)(JSF 1.2,RichFaces 3.2.1GA)
之前第一版有个问题,就是如果进行了删除等操作并立即返回列表页,被删除的项还在那,但其实数据库已经被删除了,需要做一个刷新列表操作才可以,本版就是修正此问题的。
如果要修正此问题,就需要将分页的扩展代码也就是第一版中TestBean中的代码放入业务相关管理Bean中。
页面代码(其中testBean已经变成业务Managed-bean user了):
<
f:view
>
< h:form id ="formlist" >
< rich:dataTable id ="carList" width ="483" columnClasses ="col" rows ="#{user.pageSize}"
value ="#{user.dataModel}" var ="car" >
< f:facet name ="header" >
< rich:columnGroup >
< h:column >
< h:outputText styleClass ="headerText" value ="Name" />
</ h:column >
< h:column >
< h:outputText styleClass ="headerText" value ="Decription" />
</ h:column >
< h:column >
< h:outputText styleClass ="headerText" value ="Base Price" />
</ h:column >
< h:column >
< h:outputText styleClass ="headerText" value ="Time" />
</ h:column >
< h:column >
< h:outputText styleClass ="headerText" value ="操作" />
</ h:column >
</ rich:columnGroup >
</ f:facet >
< h:column >
< h:outputText value ="#{car.name}" />
</ h:column >
< h:column >
< h:outputText value ="#{car.description}" />
</ h:column >
< h:column >
< h:outputText value ="#{car.baseprice}" />
</ h:column >
< h:column >
< h:outputText value ="#{car.timestamp}" />
</ h:column >
< h:column >
< h:commandLink action ="#{user.delete}" value ="删除" >
< f:param name ="id" value ="#{car.id}" />
</ h:commandLink >
</ h:column >
</ rich:dataTable >
< rich:datascroller for ="carList" id ="dc1"
style ="width:483px" page ="#{user.scrollerPage}" />
</ h:form >
</ f:view >
< h:form id ="formlist" >
< rich:dataTable id ="carList" width ="483" columnClasses ="col" rows ="#{user.pageSize}"
value ="#{user.dataModel}" var ="car" >
< f:facet name ="header" >
< rich:columnGroup >
< h:column >
< h:outputText styleClass ="headerText" value ="Name" />
</ h:column >
< h:column >
< h:outputText styleClass ="headerText" value ="Decription" />
</ h:column >
< h:column >
< h:outputText styleClass ="headerText" value ="Base Price" />
</ h:column >
< h:column >
< h:outputText styleClass ="headerText" value ="Time" />
</ h:column >
< h:column >
< h:outputText styleClass ="headerText" value ="操作" />
</ h:column >
</ rich:columnGroup >
</ f:facet >
< h:column >
< h:outputText value ="#{car.name}" />
</ h:column >
< h:column >
< h:outputText value ="#{car.description}" />
</ h:column >
< h:column >
< h:outputText value ="#{car.baseprice}" />
</ h:column >
< h:column >
< h:outputText value ="#{car.timestamp}" />
</ h:column >
< h:column >
< h:commandLink action ="#{user.delete}" value ="删除" >
< f:param name ="id" value ="#{car.id}" />
</ h:commandLink >
</ h:column >
</ rich:dataTable >
< rich:datascroller for ="carList" id ="dc1"
style ="width:483px" page ="#{user.scrollerPage}" />
</ h:form >
</ f:view >
DataPage.java(没有变化):
import
java.util.List;
public class DataPage
{
/** *//**
* 将需要的页的数据封装到一个DataPage中去, 这个类表示了我们需要的一页的数据,<br>
* 里面包含有三个元素:datasetSize,startRow,和一个用于表示具体数据的List。<br>
* datasetSize表示了这个记录集的总条数,查询数据的时候,使用同样的条件取count即可,<br>
* startRow表示该页的起始行在数据库中所有记录集中的位置
*/
private int datasetSize;
private int startRow;
private List data;
/** *//**
*
* @param datasetSize
* 数据集大小
* @param startRow
* 起始行
* @param data
* 数据list
*/
public DataPage(int datasetSize, int startRow, List data)
{
this.datasetSize = datasetSize;
this.startRow = startRow;
this.data = data;
}
/** *//**
*
* @return
*/
public int getDatasetSize()
{
return datasetSize;
}
public int getStartRow()
{
return startRow;
}
/** *//**
*
* @return 已填充好的数据集
*/
public List getData()
{
return data;
}
}
public class DataPage
{
/** *//**
* 将需要的页的数据封装到一个DataPage中去, 这个类表示了我们需要的一页的数据,<br>
* 里面包含有三个元素:datasetSize,startRow,和一个用于表示具体数据的List。<br>
* datasetSize表示了这个记录集的总条数,查询数据的时候,使用同样的条件取count即可,<br>
* startRow表示该页的起始行在数据库中所有记录集中的位置
*/
private int datasetSize;
private int startRow;
private List data;
/** *//**
*
* @param datasetSize
* 数据集大小
* @param startRow
* 起始行
* @param data
* 数据list
*/
public DataPage(int datasetSize, int startRow, List data)
{
this.datasetSize = datasetSize;
this.startRow = startRow;
this.data = data;
}
/** *//**
*
* @return
*/
public int getDatasetSize()
{
return datasetSize;
}
public int getStartRow()
{
return startRow;
}
/** *//**
*
* @return 已填充好的数据集
*/
public List getData()
{
return data;
}
}
PagedListDataModel.java(添加了一些注释):
import java.util.List;
import javax.faces.model.DataModel;
/** */ /**
*
* TODO 分页所使用的类
* @author <a href="mailto:[email protected]">TianLu</a>
* @version $Rev$ <br>
* $Id$
*/
/**/ /* 使用方法:
* 前台的功能模块Bean(例如User)中加入类似下面的代码,可根据您的需要进行相应修改
* private PagedListDataModel dataModel;
private int pageSize = 10;
public int getPageSize()
{
return pageSize;
}
public PagedListDataModel getDataModel()
{
if ( dataModel == null ) {
dataModel = new PagedListDataModel(pageSize)
{
//查询分页函数
public DataPage fetchPage(int startRow, int pageSize)
{
// call enclosing managed bean method to fetch the data
CarBeanDAO dao = new CarBeanDAO();
String sql = "from CarBean model order by model.id desc";
Query query = EntityManagerHelper.createQuery(sql);
query.setFirstResult(startRow);
query.setMaxResults(pageSize);
List list = query.getResultList();
System.out.println("current row count = " + list.size());
Query q = EntityManagerHelper.createQuery("select count(*) from CarBean");
return new DataPage(Integer.parseInt(q.getSingleResult().toString()), startRow, list);
}
};
}
return dataModel;
}*/
/**/ /* 前台控件像这样使用
* <rich:dataTable id="carList" width="483" columnClasses="col" rows="#{user.pageSize}"
value="#{user.dataModel}" var="car">
<f:facet name="header">
<rich:columnGroup>
<h:column>
<h:outputText styleClass="headerText" value="Name" />
</h:column>
<h:column>
<h:outputText styleClass="headerText" value="Decription" />
</h:column>
<h:column>
<h:outputText styleClass="headerText" value="Base Price" />
</h:column>
<h:column>
<h:outputText styleClass="headerText" value="Time" />
</h:column>
<h:column>
<h:outputText styleClass="headerText" value="操作" />
</h:column>
</rich:columnGroup>
</f:facet>
<h:column>
<h:outputText value="#{car.name}" />
</h:column>
<h:column>
<h:outputText value="#{car.description}" />
</h:column>
<h:column>
<h:outputText value="#{car.baseprice}" />
</h:column>
<h:column>
<h:outputText value="#{car.timestamp}" />
</h:column>
<h:column>
<h:commandLink action="#{user.delete}" value="删除" >
<f:param name="id" value="#{car.id}"/>
</h:commandLink>
</h:column>
</rich:dataTable>
<rich:datascroller for="carList" id="dc1"
style="width:483px" page="#{user.scrollerPage}"/>*/
/**/ /*
* 进行删除等操作后会立即改变列表项并且返回列表页的,请调用本类的refresh方法刷新当前页面
* 使用方法:
* dao.delete(bean);
* dataModel.refresh();
*/
public abstract class PagedListDataModel extends DataModel {
int pageSize;
int rowIndex;
DataPage page;
/** *//**
* Create a datamodel that pages through the data showing the specified
* number of rows on each page.
*/
public PagedListDataModel(int pageSize) {
super();
this.pageSize = pageSize;
this.rowIndex = -1;
this.page = null;
}
/** *//**
* Not used in this class; data is fetched via a callback to the fetchData
* method rather than by explicitly assigning a list.
*/
public void setWrappedData(Object o) {
if (o instanceof DataPage) {
this.page = (DataPage) o;
} else {
throw new UnsupportedOperationException(" setWrappedData ");
}
}
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.
*/
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!).
*/
public int getRowCount() {
return getPage().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 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 = fetchPage(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.
*/
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 = fetchPage(rowIndex, pageSize);
}
int datasetSize = page.getDatasetSize();
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 = fetchPage(rowIndex, pageSize);
startRow = page.getStartRow();
} else if (rowIndex >= endRow) {
page = fetchPage(rowIndex, pageSize);
startRow = page.getStartRow();
}
return page.getData().get(rowIndex - startRow);
}
public Object getWrappedData() {
return page.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.
*/
public boolean isRowAvailable() {
DataPage page = getPage();
if (page == null) {
return false;
}
int rowIndex = getRowIndex();
if (rowIndex < 0) {
return false;
} else if (rowIndex >= page.getDatasetSize()) {
return false;
} else {
return true;
}
}
/** *//**
* Method which must be implemented in cooperation with the managed bean
* class to fetch data on demand.
*/
public abstract DataPage fetchPage(int startRow, int pageSize);
/** *//**
* 进行删除等操作后会立即改变列表项并且返回列表页的,请调用此方法,用于刷新列表。
*/
public void refresh() {
if (this.page != null) {
this.page = null;
getPage();
}
}
}
User中的delete方法:
public
String delete()
{
FacesContext ctx = FacesContext.getCurrentInstance();
int id = Integer.parseInt(ctx.getExternalContext().getRequestParameterMap().get("id"));
EntityManagerHelper.beginTransaction();
CarBeanDAO dao = new CarBeanDAO();
CarBean bean = dao.findById(id);
if(bean != null)
{
dao.delete(bean);
dataModel.refresh();
}
EntityManagerHelper.commit();
return null;
}
{
FacesContext ctx = FacesContext.getCurrentInstance();
int id = Integer.parseInt(ctx.getExternalContext().getRequestParameterMap().get("id"));
EntityManagerHelper.beginTransaction();
CarBeanDAO dao = new CarBeanDAO();
CarBean bean = dao.findById(id);
if(bean != null)
{
dao.delete(bean);
dataModel.refresh();
}
EntityManagerHelper.commit();
return null;
}
User中scrollerPage的相关代码:
private
int
scrollerPage
=
1
;
public int getScrollerPage()
{
return scrollerPage;
}
public void setScrollerPage( int scrollerPage)
{
this.scrollerPage = scrollerPage;
System.out.println("current page = " + scrollerPage);
}
public int getScrollerPage()
{
return scrollerPage;
}
public void setScrollerPage( int scrollerPage)
{
this.scrollerPage = scrollerPage;
System.out.println("current page = " + scrollerPage);
}
Faces-config.xml:
<
managed-bean
>
< managed-bean-name > user </ managed-bean-name >
< managed-bean-class > com.gcoresoft.jsfdemo.bean.User </ managed-bean-class >
< managed-bean-scope > session </ managed-bean-scope >
</ managed-bean >
< managed-bean-name > user </ managed-bean-name >
< managed-bean-class > com.gcoresoft.jsfdemo.bean.User </ managed-bean-class >
< managed-bean-scope > session </ managed-bean-scope >
</ managed-bean >
有人反映会出现获取两次DataModel的情况,为什么呢?
经过我的测试,是因为设置的datatable的rows属性的值大于了程序中pageSize的值,比如你设置rows="12",而你的pageSize设置的是10,那么JSF为了满足显示12条得条件,就会取两次数据集组成一个12跳得数据集显示出来,所以给定的rows属性最好可以使用相关管理bean的pageSize变量,这样前后台统一数据条目,提高性能和可操作性。
为了方便大家JSF的项目开发,后面我会推出一套我自己写的一个基于JSF的框架,当然此框架是基于实际项目成功实施后提取出来的,主要是简化了对表单的操作,增加一些常用的工具套件,例如分页,验证器等等。对表单操作部分的简化主要体现在:每种控件都可以和固定的模型绑定,这样操作模型就可以做到对前台控件的控制,比如后台取值可以使用attributes.get("username"),设置前台控件值可以使用attributes.put("username","admin"),对选择控件操作也更加简便,当使用put的方法的时候,前台会自动选择到那项,更重要的是这些简化不需要你写程序代码,写一些简单的配置文件即可,而且选择项不仅可以设置在配置文件中,还可以通过数据库等其他数据源获取,大大提高开发效率。
---------------------------------------------------------
专注移动开发
Android, Windows Mobile, iPhone, J2ME, BlackBerry, Symbian