在Web应用程序里,分页总让我们开发人员感到很头疼,倒不是因为技术上有多么困难,只是本来和业务没有太多关系的这么一个问题,你却得花不少功夫来处理。要是稍不留神,时不时出点问题就更郁闷了。我现在做的一个项目也到了该处理分页的时候了,感觉以前处理得都不好,所以这次有所改变,基本目标是在现有(未分页)的代码基础上,尽量少做修改,并且同样的代码可以应用于不同模块的分页。以下就是我用的方法:
首先,考虑分页绝大多数发生在列表时,组合查询时也需要用到。在我的项目里,列表的Action一般名字为ListXXXActioin,例如客户列表是ListClientsAction等等。在未分页前,ListXXXAction里会把所有的对象取出,通过request.setAttribute()放在request里,然后将请求转向到列表的jsp(例如listClients.jsp)显示出来(你可能会说不要在Action里放业务逻辑,但现在这不是我们考虑的重点)。而分页后,我们只取用户请求页对应的那些对象。为了最大限度的达到代码重用,我做了以下工作:
1、新建一个Pager类,该类有beginPage、endPage、currentPage、pageSize和total等int类型的属性,分别代表开始页、结束页、当前页、每页记录数和总记录数,它主要是让jsp页面显示页导航使用的。请注意currentPage属性是从0开始的。
2、新建一个AbstractListActioin类,并让所有ListXXXAction都继承它。在这个类里覆盖execute()方法,可以在这里判断权限等等,并在判断权限通过后执行一个abstract的act()方法,这个act()由ListXXXAction来实现。
3、在AbstractListAction里增加getPage()方法,用来从request得到用户请求的页码(若未请求则认为是第0页):
String p = request.getParameter( " p " );
if (p == null )
return 0 ;
else
try {
return Integer.parseInt(p);
} catch (NumberFormatException e) {
return 0 ;
}
}
4、在AbstractListAction里增加makePager()方法,用来向request里增加一个Pager类的实例,供jsp页面显示页导航:
Pager pager = new Pager();
pager.setTotal(total);
pager.setPageSize(Config.getInstance().getPageSize());
pager.setBeginPage( 0 );
pager.setEndPage(((pager.getTotal()) - 1 ) / pager.getPageSize() + 1 );
pager.setCurrentPage(getPage(request));
return pager;
}
注意在我的项目里,每页记录数是写在配置文件里的,如果你没有配置文件,上面第4行setPageSize()的参数直接填数字即可,例如pager.setPageSize(10);
5、这样,所有的ListXXXAction都可以使用getPage()得到请求的页码,并且能够方便的通过makePager()构造需要放在request里的pager对象了。现在要在从数据库取数据的代码上再做一些修改,即只取所需要的那一部分数据。由于我的项目中使用了Hibernate,所以这个修改也不是很困难。未分页前,在我的ListClientsAction里是通过构造一个Query来得到全部Client的,现在,只要在构造这个Query后再加两句(setMaxResults和setFirstResult)即可:
int total = ; // 得到总记录数
Pager pager = makePager(request, total); // 调用父类中的方法构造一个Pager实例
query.setMaxResults(pager.getPageSize()); // 设置每页记录数
query.setFirstResult(pager.getCurrentPage() * pager.getPageSize()); // 设置开始位置
request.setAttribute(Pager.class.getName(), pager); // 把pager放在request里
request.setAttribute(Client.class.getName(), query.list());
目前存在一个问题,就是在上面代码的第二句中,应该是获得总记录数,但我暂时没有特别好的办法不得到全部对象而直接得到记录数,只能很恐怖的用“int total = query.list().size();”,汗……
6、最后,我写了一个页导航的jsp页面pager.jsp,供各个显示列表的jsp来include,代码如下:
< table width = " 90% " border = " 0 " align = " center " cellpadding = " 2 " cellspacing = " 1 " bgcolor = " #CCCCCC " >
< tr >
< td bgcolor = " #EEEEEE " align = " right " >
< bean:message key = " prompt.pager " arg0 = " <%= "" +pager.getTotal()%> " />
[
<%
String url = request.getRequestURL().toString();
for ( int i = pager.getBeginPage();i < pager.getEndPage();i ++ ){
if (i == pager.getCurrentPage()){
%>
<%= (i + 1 ) %>
<% } else {
String qs = request.getQueryString() == null ? "" :request.getQueryString();
String op = " p= " + pager.getCurrentPage(); // Original page parameter expression
String np = " p= " + i; // New expression
if (qs.indexOf(op) ==- 1 )
qs = np + " & " + qs;
qs = qs.replaceAll(op,np);
%>
< a href = " <%=url+ " ? " +qs%> " ><%= (i + 1 ) %></ a >
<% } %>
<% if (i < pager.getEndPage() - 1 ){ %>
& nbsp;
<% } %>
<% } %>
]
</ td ></ tr >
</ table >
我觉得有必要解释一下,在上面的代码中,关于每一页对应的url是这样处理。取request.getRequestURL().toString()和request.getQueryString(),其中前者是不需要变的,而后者中可能包含“q=2”这样的页码请求也可能不包含即缺省请求第0页,所以统一用replaceAll()方法将其去掉,然后将对应的页码请求串(如“q=3”)加在qs的前面。这样做的好处是,每个模块都可以使用这个页导航,并且不会丢失url中的其他参数(例如今后加入排序功能后,url中可能包含“dir=desc”这样的参数)。
在列表jsp(listClients.jsp)中,很简单的这样include它(之所以要放在<logic:notEmpty>里,是希望在没有记录可显示的时候就不显示页导航了):
<% @include file = " /pager.jsp " %>
</ logic:notEmpty >
经过上面几步的处理,我的客户列表已经可以实现分页了,效果见下图。如果在另外一个模块中也需要分页,比如部门列表时,只需要1、修改ListDeptsAction继承AbstractListAction,2、在ListDeptsAction里增加setMaxResults()和setFirstResults()方法,3、在listDepts.jsp中适当的位置include页导航,就可以了,改动是相当小的。
最后,如果希望组合查询的结果也能够分页,必须指定组合查询表单的method属性为“GET”,这样查询要求会被记录在url中,分页导航从而能够正常的工作(每次换页都将查询要求和请求的页码提交)。
在struts中分页的一种实现
我的项目中的分页功能
1, 思路
使用一个页面控制类,它记录页面信息,如上页,下页,当前页等。在查询的Action中,将这个控制类和查询条件一起传递给数据库访问bean,然后将这两个参数保存在用户session中。在分页控制Action中,利用接收到的分页参数调用数据库访问的bean.
2,实现
(1)分页控制类
/* @author nick
* Created on 2004-3-18
* file name:PageController.java
*
*
*/
package com.tower.util;
/**
* @author nick
* 2004-3-18
* 用来进行翻页控制
*
*/
public class PageController {
int totalRowsAmount; //总行数
boolean rowsAmountSet; //是否设置过totalRowsAmount
int pageSize=2; //每页行数
int currentPage=1; //当前页码
int nextPage;
int previousPage;
int totalPages; //总页数
boolean hasNext; //是否有下一页
boolean hasPrevious; //是否有前一页
String description;
int pageStartRow;
int pageEndRow;
public PageController(int totalRows){
setTotalRowsAmount(totalRows);
}
public PageController(){}
/**
* @param i
* 设定总行数
*/
public void setTotalRowsAmount(int i) {
if(!this.rowsAmountSet){
totalRowsAmount = i;
totalPages=totalRowsAmount/pageSize+1;
setCurrentPage(1);
this.rowsAmountSet=true;
}
}
/**
* @param i
*
* 当前页
*
*/
public void setCurrentPage(int i) {
currentPage = i;
nextPage=currentPage+1;
previousPage=currentPage-1;
//计算当前页开始行和结束行
if(currentPage*pageSize<totalRowsAmount){
pageEndRow=currentPage*pageSize;
pageStartRow=pageEndRow-pageSize+1;
}else{
pageEndRow=totalRowsAmount;
pageStartRow=pageSize*(totalPages-1)+1;
}
//是否存在前页和后页
if (nextPage>totalPages){
hasNext=false;
}else{
hasNext=true;
}
if(previousPage==0){
hasPrevious=false;
}else{
hasPrevious=true;
};
System.out.println(this.description());
}
/**
* @return
*/
public int getCurrentPage() {
return currentPage;
}
/**
* @return
*/
public boolean isHasNext() {
return hasNext;
}
/**
* @return
*/
public boolean isHasPrevious() {
return hasPrevious;
}
/**
* @return
*/
public int getNextPage() {
return nextPage;
}
/**
* @return
*/
public int getPageSize() {
return pageSize;
}
/**
* @return
*/
public int getPreviousPage() {
return previousPage;
}
/**
* @return
*/
public int getTotalPages() {
return totalPages;
}
/**
* @return
*/
public int getTotalRowsAmount() {
return totalRowsAmount;
}
/**
* @param b
*/
public void setHasNext(boolean b) {
hasNext = b;
}
/**
* @param b
*/
public void setHasPrevious(boolean b) {
hasPrevious = b;
}
/**
* @param i
*/
public void setNextPage(int i) {
nextPage = i;
}
/**
* @param i
*/
public void setPageSize(int i) {
pageSize = i;
}
/**
* @param i
*/
public void setPreviousPage(int i) {
previousPage = i;
}
/**
* @param i
*/
public void setTotalPages(int i) {
totalPages = i;
}
/**
* @return
*/
public int getPageEndRow() {
return pageEndRow;
}
/**
* @return
*/
public int getPageStartRow() {
return pageStartRow;
}
public String getDescription(){
String description="Total:"+this.getTotalRowsAmount()+
" items "+this.getTotalPages() +" pages";
// this.currentPage+" Previous "+this.hasPrevious +
// " Next:"+this.hasNext+
// " start row:"+this.pageStartRow+
// " end row:"+this.pageEndRow;
return description;
}
public String description(){
String description="Total:"+this.getTotalRowsAmount()+
" items "+this.getTotalPages() +" pages,Current page:"+
this.currentPage+" Previous "+this.hasPrevious +
" Next:"+this.hasNext+
" start row:"+this.pageStartRow+
" end row:"+this.pageEndRow;
return description;
}
public static void main(String args[]){
PageController pc=new PageController(3);
System.out.println(pc.getDescription());
// pc.setCurrentPage(2);
// System.out.println(pc.description());
// pc.setCurrentPage(3);
// System.out.println(pc.description());
}
}
(2)查询Action的代码片断
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
Base queryForm= (Base) form;
if(!queryForm.getName().equals("")){
PageController pc=new PageController();
EmployeeBase service=new EmployeeBase();
ArrayList result=(ArrayList)service.search(queryForm,pc);
HttpSession session=request.getSession();
session.setAttribute("queryForm",queryForm);
session.setAttribute("pageController",service.getPageController());
request.setAttribute("queryResult",result);
request.setAttribute("pageController",service.getPageController());
return mapping.findForward("haveResult");
}else{
return mapping.findForward("noResult");
}
}
(3),翻页Action的代码片断
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
//读取翻页参数
TurnPageForm turnPageForm=(TurnPageForm)form;
//从PageController中取出查询信息,并使用bean提供的调用接口处理结果
HttpSession session=request.getSession();
PageController pc=(PageController)session.getAttribute("pageController");
Base queryForm=(Base)session.getAttribute("queryForm");
pc.setCurrentPage(turnPageForm.getViewPage());
EmployeeBase service=new EmployeeBase();
ArrayList result=(ArrayList)service.search(queryForm,pc);
//根据参数将数据写入 request
request.removeAttribute("queryResult");
request.removeAttribute("pageController");
request.setAttribute("queryResult",result);
request.setAttribute("pageController",pc);
//forward 到显示页面
return mapping.findForward("haveResult");
}
(4)数据库访问bean中的片断
public Collection search(Base base, PageController pc)
throws SQLException {
ArrayList emps = new ArrayList();
ResultSet rs = getSearchResult(base);
rs.absolute(-1);
pc.setTotalRowsAmount(rs.getRow());
setPageController(pc);
if (rs.getRow() > 0) {
rs.absolute(pc.getPageStartRow());
do {
System.out.println("in loop" + rs.getRow());
Base b = new Base();
b.setName(rs.getString("Name"));
b.setIdCard(rs.getString("IDCard"));
System.out.println("From db:" + rs.getString("IDCard"));
emps.add(b);
if (!rs.next()) {
break;
}
} while (rs.getRow() < (pc.getPageEndRow() + 1));
}
return emps;
}
(5)在jsp中,翻页部分的代码片断
<bean:write name="pageController" property="description"/>
<logic:equal name="pageController" property="hasPrevious" value="true">
<a href="turnPage.do?viewPage=<bean:write name="pageController" property="previousPage"/>" class="a02">
Previous
</a>
</logic:equal>
<logic:equal name="pageController" property="hasNext" value="true">
<a href="turnPage.do?viewPage=<bean:write name="pageController" property="nextPage"/>" class="a02">
Next
</a>
</logic:equal>
这样一来,翻页的功能可以以你喜欢的方式表现给client