应用Myfaces 进行On-demand loading分页
第一章
引入myfaces的其中一个t标签:
<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>
<t:dataTable id="data"
style
header
footerrowClasses="standardTable_Row1,standardTable_Row2" columnClasses="standardTable_Column,standardTable_ColumnCentered,standardTable_Column"
var="car" value="#{scrollerList.list}"
preserveDataModel="false" rows="10"></t:dataTable>
讲解:
t:dataTable标签是用来分页输出List数组
id定义分页表的id,
styleClass定义表的样式css类
headerClass定义表头的样式css类
footerClass定义表尾的样式css类
rowClasses定义行的样式css类
columnClasses定义列的样式css类
var定义每一条记录
value指定要分页的数据
rows指定每页显示数据
<t:dataScroller id="scroll_1" for="data" fastStep="10"
pageCountVar="pageCount" pageIndexVar="pageIndex"
stylepaginator="true" paginatorMaxPages="5"
paginatorTableimmediate="true"
actionListener="#{scrollerList.scrollerAction}">
</t: dataScroller >
讲解:
t:dataScroller显示分页信息
id定义分页格式id
for指向数据源
fastStep向前向后快进的页数
pageCountVar总页数
pageIndexVar当前页数
styleClass定义分页格式的css类
paginator是否显示页数变化轨迹
paginatorMaxPages指定页数变化轨迹中的数量
第二章
应用Myfaces 进行On-demand loading分页
index.jsp>>>
<%@ page session="false" contentType="text/xml;charset=utf-8"%>
<%
response.sendRedirect("dataScroller.jsf");
%>
dataScroller.jsp>>>页面中的图片和css请参见myfaces.apache.org的simple
<%@ page session="false" contentType="text/html;charset=utf-8"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>
<html>
<%@include file="inc/head.inc"%>
<body>
<f:view>
<h:form>
<f:loadBundle
basename="org.apache.myfaces.examples.resource.example_messages"
var="example_messages" />
<h:panelGroup id="body">
<t:dataTable id="data" stylevalue="#{scrollerList.dataModel}" preserveDataModel="true"
rows="10">
<h:column>
<f:facet name="header">
</f:facet>
<h:outputText value="#{car.id}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{example_messages['label_cars']}" />
</f:facet>
<h:outputText value="#{car.type}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{example_messages['label_color']}" />
</f:facet>
<h:outputText value="#{car.color}" />
</h:column>
</t:dataTable>
<h:panelGrid columns="1" stylefor="data" fastStep="10"
pageCountVar="pageCount" pageIndexVar="pageIndex"
stylepaginator="true" paginatorMaxPages="5"
paginatorTable
actionListener="#{scrollerList.scrollerAction}">
<f:facet name="first">
<t:graphicImage url="images/arrow-first.gif" border="1" />
</f:facet>
<f:facet name="last">
<t:graphicImage url="images/arrow-last.gif" border="1" />
</f:facet>
<f:facet name="previous">
<t:graphicImage url="images/arrow-previous.gif" border="1" />
</f:facet>
<f:facet name="next">
<t:graphicImage url="images/arrow-next.gif" border="1" />
</f:facet>
<f:facet name="fastforward">
<t:graphicImage url="images/arrow-ff.gif" border="1" />
</f:facet>
<f:facet name="fastrewind">
<t:graphicImage url="images/arrow-fr.gif" border="1" />
</f:facet>
</t:dataScroller>
<t:dataScroller id="scroll_2" for="data" rowsCountVar="rowsCount"
displayedRowsCountVar="displayedRowsCountVar"
firstRowIndexVar="firstRowIndex" lastRowIndexVar="lastRowIndex"
pageCountVar="pageCount"
pageIndexVar="pageIndex">
<h:outputFormat value="#{example_messages['dataScroller_pages']}"
style/>
<f:param value="#{displayedRowsCountVar}" />
<f:param value="#{firstRowIndex}" />
<f:param value="#{lastRowIndex}" />
<f:param value="#{pageIndex}" />
<f:param value="#{pageCount}" />
</h:outputFormat>
</t:dataScroller>
</h:panelGrid>
</h:panelGroup>
<t:commandLink value="test" immediate="true" />
</h:form>
</f:view>
</body>
</html>
Car.java>>>
package org.apache.myfaces.examples.listexample;
import java.io.Serializable;
public class Car implements Serializable {
/**
* serial id for serialisation versioning
*/
private static final long serialVersionUID = 1L;
private String id;
private String type;
private String color;
public Car() {
}
public Car(String id, String type, String color) {
this.id = id;
this.type = type;
this.color = color;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
DataPage.java>>>
package org.apache.myfaces.examples.listexample;
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;
/** */
/**
* Create an object representing a sublist of a dataset.
*
* @param datasetSize
* is the total number of matching rows available.
*
* @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 datasetSize, int startRow, List data) {
this.datasetSize = datasetSize;
this.startRow = startRow;
this.data = data;
}
/** */
/**
* Return the number of items in the full dataset.
*/
public int getDatasetSize() {
return datasetSize;
}
/** */
/**
* 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 getData() {
return data;
}
}
DataScrollerList.java>>>
package org.apache.myfaces.examples.listexample;
import java.util.ArrayList;
import java.util.List;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.DataModel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.custom.datascroller.ScrollerActionEvent;
import org.apache.myfaces.examples.dao.CarDAO;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DataScrollerList extends BasePagedBackingBean {
private final Log log = LogFactory.getLog("DataScrollerList");
private CarDAO carDAO = (CarDAO) getBean("carDAOBean");
public DataScrollerList() {
log.info("创建 DataScroller");
}
public void scrollerAction(ActionEvent event) {
ScrollerActionEvent scrollerEvent = (ScrollerActionEvent) event;
FacesContext.getCurrentInstance().getExternalContext().log(
"scrollerAction: facet: " + scrollerEvent.getScrollerfacet()
+ ", pageindex: " + scrollerEvent.getPageIndex());
}
public int getTotalCount() {
int totalCount = 0;
totalCount = carDAO.getTotalCount();
return totalCount;
}
/**
* 在DataScrollerList这个 Backing Bean中加一些东西,<br>
* 调用业务逻辑,并将数据交给PagedListDataModel,来帮我们完成最后的分页工作。
*/
public DataPage getDataPage(int startRow, int pageSize) {
DataPage dataPage = carDAO.getDataPage(startRow, pageSize);
return dataPage;
}
}
BasePagedBackingBean.java>>>
package org.apache.myfaces.examples.listexample;
import javax.faces.model.DataModel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public abstract class BasePagedBackingBean {
private final Log log = LogFactory.getLog("BasePagedBackingBean");
protected abstract DataPage getDataPage(int startRow, int pageSize);
public abstract int getTotalCount();
private DataModel dataModel;
private int i = 0;
// 为什么getDataModel这个方法要调用两次?非常不解啊
public DataModel getDataModel() {
i++;
log.info("第" + i + "次调用 getDataModel.");
if (dataModel == null) {
log.info("创建 DataModel");
dataModel = new LocalDataModel(10);
}
return dataModel;
}
public Object getBean(String beanName) {
ApplicationContext ac = new ClassPathXmlApplicationContext(
"spring/applicationContext.xml");
Object ob = (Object) ac.getBean(beanName);
return ob;
}
private class LocalDataModel extends PagedListDataModel {
public LocalDataModel(int pageSize) {
super(pageSize);
}
public int fetchRowCount() {
return getTotalCount();
}
public DataPage fetchPage(int startRow, int pageSize) {
// call enclosing managed bean method to fetch the data
return getDataPage(startRow, pageSize);
}
}
}
PagedListDataModel.java>>>
package org.apache.myfaces.examples.listexample;
import javax.faces.model.DataModel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/** */
/**
* 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.
*/
/**
* 这个类里面的方法被myfaces<t:dataTable>调用顺序为:<br>
* 1.构造函数public PagedListDataModel(int pageSize) <br>
* 2.getRowCount()<br>
* 3.setRowIndex()<br>
* 4.public boolean isRowAvailable()<br>
* 5.public Object getRowData()
*/
public abstract class PagedListDataModel extends DataModel {
private final Log log = LogFactory.getLog("PagedListDataModel");
int pageSize;
int rowIndex;
private int rowCount = -1;
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() {
if (rowCount < 0) {
log.info("默认rowCount:" + rowCount);
rowCount = fetchRowCount();
log.info("初始化rowCount:" + rowCount);
}
return rowCount;
}
/**
* 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(String name) {
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
log.info("getPage:" + name + "创建page");
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);
rowCount = page.getDatasetSize();//
log.info("getRowData:创建page");
}
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) {
log.info("fetchPage:向前取数据,getRowData:创建page,rowIndex:" + rowIndex);
page = fetchPage(rowIndex, pageSize);
log.info("翻页之前rowCount:" + rowCount);
rowCount = page.getDatasetSize();//
log.info("翻页之后rowCount:" + rowCount);
startRow = page.getStartRow();
} else if (rowIndex >= endRow) {
log.info("fetchPage:向后取数据,getRowData:创建page,rowIndex:" + rowIndex);
page = fetchPage(rowIndex, pageSize);
log.info("翻页之前rowCount:" + rowCount);
rowCount = page.getDatasetSize();//
log.info("翻页之后rowCount:" + rowCount);
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("isRowAvailable");
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 abstract int fetchRowCount();
}
CarDAO.java>>>
package org.apache.myfaces.examples.dao;
import org.apache.myfaces.examples.listexample.DataPage;
public interface CarDAO {
public DataPage getDataPage(int startRow, int pageSize);
public int getTotalCount();
}
CarDAOImpl.java>>>
package org.apache.myfaces.examples.dao.impl;
import java.util.List;
import org.apache.myfaces.examples.dao.CarDAO;
import org.apache.myfaces.examples.listexample.DataPage;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class CarDAOImpl extends HibernateDaoSupport implements CarDAO {
public DataPage getDataPage(int startRow, int pageSize) {
DataPage page = new DataPage(getCountCars(), startRow, getCars(
startRow, pageSize));
return page;
}
public int getTotalCount() {
return getCountCars();
}
public List getCars(int startRow, int pageSize) {
Session session = this.getSession();
String sql = "from Car";
Query query = session.createQuery(sql);
query.setFirstResult(startRow);
query.setMaxResults(pageSize);
List list = query.list();
return list;
}
public int getCountCars() {
String sql = "select count(*) from Car";
List list = getHibernateTemplate().find(sql);
int count = 0;
if (list.size() > 0) {
count = ((Long) list.get(0)).intValue();
}
return count;
}
}
配置文件:
web.xml>>>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<listener>
<listener-class>
org.apache.myfaces.webapp.StartupServletContextListener
</listener-class>
</listener>
<listener>
<listener-class>
org.springframework.web.util.Log4jConfigListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/spring/applicationContext.xml
</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>60000</param-value>
</context-param>
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>
org.springframework.web.context.ContextLoaderServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
</web-app>
Car.hbm.xml>>>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<hibernate-mapping>
<class name="org.apache.myfaces.examples.listexample.Car" table="CAR">
<id name="id" type="java.lang.String">
<column name="ID" precision="22" scale="0" />
<generator />
</id>
<property name="type" type="java.lang.String">
<column name="TYPE" length="20" />
</property>
<property name="color" type="java.lang.String">
<column name="COLOR" length="20" />
</property>
</class>
</hibernate-mapping>
faces-config.xml>>>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
<!-- Managed Beans for dataScroller.jsp -->
<managed-bean>
<managed-bean-name>scrollerList</managed-bean-name>
<managed-bean-class>
org.apache.myfaces.examples.listexample.DataScrollerList
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<navigation-rule>
<navigation-case>
<from-outcome>go_datascroller</from-outcome>
<to-view-id>/dataScroller.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
applicationConfig.xml>>>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--
<bean id="dataSource"
/>
<property name="url"
value="jdbc:oracle:thin:@localhost:1521:oracle" />
<property name="username" value="vst" />
<property name="password" value="vst" />
<property name="maxActive" value="100" />
<property name="maxIdle" value="30" />
<property name="maxWait" value="1000" />
</bean>
<bean id="sessionFactory"
/>
</property>
<property name="mappingResources">
<list>
<value>org\apache\myfaces\examples\listexample\Car.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.OracleDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
/>
</property>
</bean>
<bean id="carDAO"
/>
</property>
</bean>
<bean id="carDAOBean"
/>
</property>
<property name="target">
<ref local="carDAO" />
</property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
</beans>
log4j.properties>>>
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
#log4j.logger.uk.ltd.getahead.dwr = ERROR
log4j.logger.org.hibernate = ERROR
log4j.logger.org.springframework= ERROR
log4j.logger.org.apache.myfaces= ERROR