改写JSF 分页控件使其能支持大容量数据分页以及查询分页

 上一篇对分页的分页的封装是基于struts框架。如果要利用JSF组件实现类似的功能又该如何呢。我对JSF的痴迷始于半年以前,如今有所降温。当时对已有的分页组件作了较大的修改,并使其投入应用。 

对于JSF的学习我开始是看Core Java Server Faces和OReilly的一本Java Server Faces,个人觉得入门看前者是个不错的选择,本文关于分页组件的改写就基于Core Java Server Faces的一个分页组件的例子。

原例子是讲述JSF组件编写过程的好教材,但是其实用性不强,主要是:它把所有数据一次性的读入内存再进行分页,所以不支持大容量的分页;它不支持查询结果分页。


先贴上页面部分的代码:

 

<%@ page language="java" contentType="text/html; charset=GBK"%> <%@ page import="com.inventec.cmm.QueryBean" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://corejsf.com/pager" prefix="corejsf" %> <html> <f:view> <font color="red"><h:outputText value="#{queryBean.msg}"/></font> <HEAD><TITLE >中獎用戶信息查詢</TITLE> <META http-equiv=Content-Type content="text/html; charset=gb2312"> <link rel="stylesheet" type="text/css" href="style.css"> <script language="JavaScript" src="calendar.js" Type="Text/JavaScript" charset="gb2312" ></script> <LINK href=style.css type=text/css rel=stylesheet> </HEAD> <BODY> <TABLE cellSpacing=0 cellPadding=0 align=center bgColor=#EAEFF3 border="datagrid" width=60%> <h:form id="frmQuery"> <TR align=middle > <TD bgColor=#2153aa colSpan=7 height=34> <DIV id=reg> <font color="#FFFFD1"><STRONG>中獎信息查詢</STRONG></font> </DIV></TD></TR> <TR> <Th align=middle height=12>開始時間</Th> <TD align=center><IMG height=1 src="pics/empty.gif" width=10><h:inputText id="DATE_BEGIN" value="#{queryBean.dateBegin}" onfocus="calendar()" /><IMG height=1 src="pics/empty.gif" width=10></TD> <Th align=middle height=12 > 結束時間</Th> <TD align=center><IMG height=1 src="pics/empty.gif" width=10><h:inputText id="DATE_END" value="#{queryBean.dateEnd}" onfocus="calendar()" /><IMG height=1 src="pics/empty.gif" width=10></TD> <th align=center> 得獎用戶<IMG height=1 src="pics/empty.gif" width=10> <h:selectBooleanCheckbox value="#{queryBean.onlyPrize}" readonly="true"/> <IMG height=1 src="pics/empty.gif" width=10></th> <th align=center> 辦事處 </th> <td align=center> <IMG height=1 src="pics/empty.gif" width=10> <h:selectOneMenu id="city" required="true" value="#{queryBean.org}" > <f:selectItems value="#{queryBean.items}"/> </h:selectOneMenu><IMG height=1 src="pics/empty.gif" width=10> </td> </TR> <tr> <th>PSID號碼</th> <td align=center><IMG height=1 src="pics/empty.gif" width=5><h:inputText id="PSID" value="#{queryBean.psid}" /> <IMG height=1 src="pics/empty.gif" width=10></td> <th>驗證碼</th> <td align=center><IMG height=1 src="pics/empty.gif" width=10><h:inputText id="PWD" value="#{queryBean.pwd}" /><IMG height=1 src="pics/empty.gif" width=10></td> <td></td><th>電話號碼</th> <td align=center><IMG height=1 src="pics/empty.gif" width=10><h:inputText id="TEL" value="#{queryBean.tel}" /><IMG height=1 src="pics/empty.gif" width=10></td> </tr> <TR > <TD align=middle height=35 colspan=7><h:commandButton type="submit" value="查詢" id="Submit" actionListener="#{queryBean.reset}" /> <h:commandButton type="submit" value="导出" actionListener="#{queryBean.export}" /> </TD></TR> </h:form> <tr bgcolor=#fffff> <td align=center colspan=7 bgcolor=#FFFFD1>**提示:如果需要查看2006-10-1號信息,開始時間輸入2006-10-1,結束時間輸入2006-10-2</td></tr></table><p><h:outputText /> <p> <h:form id="frmData"> <h:dataTable styleClass="datagrid" cellspacing="0" cellpadding="1" border="1" id="userList" value="#{queryBean.dataModel}" var="p" rows="50" width="100%" rowClasses="evenColumn,oddColumn"> <h:column> <f:facet name="header"> <h:outputText value="序號"/> </f:facet> <h:outputText value="#{p.id}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="PSID"/> </f:facet> <h:outputText value="#{p.psid}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="姓名"/> </f:facet> <h:outputText value="#{p.name}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="獎項"/> </f:facet> <h:outputText value="#{p.prizeCode}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="電話號碼"/> </f:facet> <h:outputText value="#{p.tel}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="領獎號碼"/> </f:facet> <h:outputText value="#{p.telPrize}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="驗證碼"/> </f:facet> <h:outputText value="#{p.pwd}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="办事处"/> </f:facet> <h:outputText value="#{p.orgName}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="參與時間"/> </f:facet> <h:outputText value="#{p.joinDate}" /> </h:column> </h:dataTable> <corejsf:pager dataTableId="userList" showpages="20" selectedStyleClass="currentPage"/> </h:form> </BODY> </f:view> </HTML>

 

  
在<h:form>标签中,我依次放置了:若干查询参数的输入控件;查询列表的table控件,还有就是本文的主角了:<corejsf:pager >表示的分页控件。

分页控件的dataTableId属性关联了相对应的查询列表控件,以控制其显示。 我将后台的数据都封装在一个queryBean当中,这里没有进行更多的逻辑分层,仅为简化起见,特此说明。

 

在queryBean当中,有几个重要的函数,以及一个内部类

 

public void reset(javax.faces.event.ActionEvent e){ ((LocalDataModel)dataModel).resetPage(0,50); FacesContext facesContext = FacesContext.getCurrentInstance(); UIComponent c =facesContext.getViewRoot(); UIData data=null; String name="frmData:userList"; c=findComponent(c,name,facesContext); if (c!=null){ data=(UIData)c; data.setFirst(0); } } private UIComponent findComponent(UIComponent component, String id, FacesContext context) { String componentId = component.getClientId(context); if (componentId.equals(id)) return component; Iterator kids = component.getChildren().iterator(); while (kids.hasNext()) { UIComponent kid = (UIComponent) kids.next(); UIComponent found = findComponent(kid, id, context); if (found != null) return found; } return null; } public DataModel getDataModel() { if (dataModel == null ) { dataModel = new LocalDataModel(50); } return dataModel; } private class LocalDataModel extends PagedListDataModel { public LocalDataModel( int pageSize) { super (pageSize); } public DataPage fetchPage( int startRow, int pageSize) { return getDataPage(startRow, pageSize); } }

 

 

其中findComponent函数被用来根据服务器端id“寻找”form范围内的控件,这是个递归寻找的过程。

LocalDataModel  这个内部类可以说是整个控件改写的“灵魂”,它继承于PagedListDataModel   类,而该类实现了javax.faces.model.DataModel的接口。对于该接口的特定实现,可以解决大容量的分页的问题,同时兼顾代码结构的优雅,以及分页逻辑的完全隔离(与业务无关)。

reset函数的作用在于:当用户点查询的时候,所有之前的查询参数清0,记录定位到新的查询结果的第1条记录。

 

PagedListDataModel是个虚类,里面有两个重要函数:

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==-3){ setRowIndex(0); page=fetchPage(rowIndex, pageSize); return null; } ///////////////////////////////////////////////////// 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 abstract DataPage fetchPage(int startRow, int pageSize);中留待用户自己实现。我把这个在queryBean当中实现,

具体的比如说用分页的SQL语句直接在数据库端得到数据,也可以用hibernate,这个取决于自己的实际情况。

 

再贴一下控件部分的代码,主要是PageRender类里面encodeBegin和decode函数

public void encodeBegin(FacesContext context, UIComponent component) throws IOException { String id = component.getClientId(context); UIComponent parent = component; while (!(parent instanceof UIForm)) parent = parent.getParent(); String formId = parent.getClientId(context); ResponseWriter writer = context.getResponseWriter(); String styleClass = (String) get(context, component, "styleClass"); String selectedStyleClass = (String) get(context, component, "selectedStyleClass"); String dataTableId = (String) get(context, component, "dataTableId"); Integer a = (Integer) get(context, component, "showpages"); int showpages = a == null ? 0 : a.intValue(); // find the component with the given ID UIData data = (UIData) findComponent(context.getViewRoot(), getId(dataTableId, id), context); int first=0; int itemcount=0; int pagesize=20; if (data!=null){ first = data.getFirst(); itemcount = data.getRowCount(); pagesize = data.getRows(); }else{ // Operation opt=new Operation(); // itemcount=opt.getRowCount(" CMM_club_prize_joined_users "); } if (pagesize <= 0) pagesize = itemcount; int pages = itemcount / pagesize; if (itemcount % pagesize != 0) pages++; int currentPage = first / pagesize; if (first >= itemcount - pagesize) currentPage = pages - 1; int startPage = 0; int endPage = pages; if (showpages > 0) { startPage = (currentPage / showpages) * showpages; endPage = Math.min(startPage + showpages, pages); } writer.write("共计"+pages+"页"+itemcount+"条记录"); if (currentPage > 0) writeLink(writer, component, formId, id, "<", styleClass); if (startPage > 0) writeLink(writer, component, formId, id, "<<", styleClass); for (int i = startPage; i < endPage; i++) { writeLink(writer, component, formId, id, "" + (i + 1), i == currentPage ? selectedStyleClass : styleClass); } if (endPage < pages) writeLink(writer, component, formId, id, ">>", styleClass); if (first < itemcount - pagesize) writeLink(writer, component, formId, id, ">", styleClass); writeInput(writer,component,formId,id,styleClass); // FacesContext facesContext = FacesContext.getCurrentInstance(); // hidden field to hold result writeHiddenField(writer, component, id); } public void decode(FacesContext context, UIComponent component) { String id = component.getClientId(context); Map parameters = context.getExternalContext() .getRequestParameterMap(); String response = (String) parameters.get(id); String dataTableId = (String) get(context, component, "dataTableId"); Integer a = (Integer) get(context, component, "showpages"); int showpages = a == null ? 0 : a.intValue(); UIData data = (UIData) findComponent(context.getViewRoot(), getId(dataTableId, id), context); int first = data.getFirst(); int itemcount = data.getRowCount(); int pagesize = data.getRows(); if (pagesize <= 0) pagesize = itemcount; if (response.equals("<")) first -= pagesize; else if (response.equals(">")) first += pagesize; else if (response.equals("<<")) first -= pagesize * showpages; else if (response.equals(">>")) first += pagesize * showpages; else { int page=0; int num=convertNum(response); if ((num>0)&&(num<=itemcount/pagesize+1)){ page = Integer.parseInt(response); first = (page - 1) * pagesize; } } if (first < 0) first = 0; data.setFirst(first); }

 

贴张投入应用的图:

你可能感兴趣的:(数据结构,css,JSF,F#,CMM)