Fork me on GitHub

Java分页技术

对于分页技术,我在两个项目中做过。而这两个项目中的分页技术也略有不同。

第一种

第一种分页技术是我在一个网上图书商城项目用的分页技术。包含了PageBean和JSP的代码实现。闲话不多说,先贴上代码:

PageBean类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package yh.mall.pager;
import java.util.List;
/**
* 分页Bean,它会在各层之间传递
* @author YH
*
* @param <T>
*/
public class PageBean<T> {
private int pc; //当前页码
private int tr; //总记录数
private int ps; //每页记录数
//请求路径和参数,例如:/BookServlet?method=findXXX&cid=1&bname=2
private String url;
private List<T> beanList; //要写成泛型类,这样就是一个通用Bean了
//计算总页数
public int getTp(){
int tp = tr/ps;
return tr % ps == 0 ? tp : tp+1;
}
public int getPc() {
return pc;
}
public void setPc(int pc) {
this.pc = pc;
}
public int getTr() {
return tr;
}
public void setTr(int tr) {
this.tr = tr;
}
public int getPs() {
return ps;
}
public void setPs(int ps) {
this.ps = ps;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public List<T> getBeanList() {
return beanList;
}
public void setBeanList(List<T> beanList) {
this.beanList = beanList;
}
@Override
public String toString() {
return "PageBean [beanList=" + beanList + ", pc=" + pc + ", ps=" + ps
+ ", tr=" + tr + ", url=" + url + "]";
}
}

PageConstants类:用于设置常量如:图书每页记录数、订单每页记录数

1
2
3
4
5
6
7
package yh.mall.pager;
public class PageConstants {
public static final int BOOK_PAGE_SIZE = 12;// 图书每页记录数
public static final int Order_PAGESIZE = 8;// 订单每页记录数
}

Expression类(后面的dao层会用到,用于sql的where):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package yh.mall.pager;
public class Expression {
private String name;
private String operator;
private String value;
public Expression() {
super();
// TODO Auto-generated constructor stub
}
public Expression(String name, String operator, String value) {
super();
this.name = name;
this.operator = operator;
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
return "Express [name=" + name + ", operator=" + operator + ", value="
+ value + "]";
}
}

pager.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<script type="text/javascript">
function _go() {
var pc = $("#pageCode").val();//获取文本框中的当前页码
if(!/^[1-9]\d*$/.test(pc)) {//对当前页码进行整数校验
alert('请输入正确的页码!');
return;
}
if(pc > ${pb.tp }) {//判断当前页码是否大于最大页
alert('请输入正确的页码!');
return;
}
location = "${pb.url }&pc="+pc;
}
</script>
<div class="divBody">
<div class="divContent">
<%--上一页,2个上一页,一个是超链接,一个不是,按照需求显示其中一个 --%>
<c:choose>
<c:when test="${pb.pc eq 1 }"><span class="spanBtnDisabled">上一页</span></c:when>
<c:otherwise>
<a href="${pb.url }&pc=${pb.pc-1}" class="aBtn bold">上一页</a>
</c:otherwise>
</c:choose>
<%-- 计算beginend --%>
<%-- 如果总页数<=6,那么显示所有页码,即begin=1 end=${pb.tp} --%>
<%-- 设置begin=当前页码-2end=当前页码+3 --%>
<%-- 如果begin<1,那么让begin=1 end=6 --%>
<%-- 如果end>最大页,那么begin=最大页-5 end=最大页 --%>
<c:choose>
<c:when test="${pb.tp<=6 }">
<c:set var="begin" value="1"></c:set>
<c:set var="end" value="${pb.tp }"></c:set>
</c:when>
<c:otherwise>
<c:set var="begin" value="${pb.pc-2 }"></c:set>
<c:set var="end" value="${pb.pc+3 }"></c:set>
<c:if test="${begin<1 }">
<c:set var="begin" value="1"></c:set>
<c:set var="end" value="6"></c:set>
</c:if>
<c:if test="${end>pb.tp }">
<c:set var="begin" value="${pb.tp-5 }"></c:set>
<c:set var="end" value="${pb.tp }"></c:set>
</c:if>
</c:otherwise>
</c:choose>
<c:forEach begin="${begin }" end="${end }" var="i">
<c:choose>
<c:when test="${i eq pb.pc }">
<span class="spanBtnSelect">${i }</span>
</c:when>
<c:otherwise>
<a href="${pb.url }&pc=${i }" class="aBtn">${i }</a>
</c:otherwise>
</c:choose>
</c:forEach>
<%-- 显示点点点 --%>
<c:if test="${end < pb.tp}"><span class="spanApostrophe">...</span></c:if>
<%--下一页 --%>
<c:choose>
<c:when test="${pb.pc eq pb.tp }">
<span class="spanBtnDisabled">下一页</span>
</c:when>
<c:otherwise>
<a href="${pb.url }&pc=${pb.pc+1}" class="aBtn bold">下一页</a>
</c:otherwise>
</c:choose>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<%-- 共N页 到M页 --%>
<span>共${pb.tp }页</span>
<span></span>
<input type="text" class="inputPageCode" id="pageCode" value="${pb.pc }"/>
<span></span>
<a href="javascript:_go();" class="aSubmit">确定</a>
</div>
</div>

分页JSP在其他页面的插入位置:

1
2
3
4
5
6
7
<!-- 用了<%@include%>标签包含在其他页面当中 -->
<div style="float:left; width: 100%; text-align: center;">
<hr/>
<br/>
<!-- 静态包含include,包含了一个pager页面 -->
<%@include file="/jsps/pager/pager.jsp" %>
</div>

JSP页面效果展示:

上述pager.jsp中的其他代码就不说了,在上面写得都很明白。主要说下上一页,下一页以及每页和确定中的url,按上一页举例:
href=”${pb.url }&pc=${pb.pc-1}”
可以说整个分页技术的核心就在这个url上了。
就拿我当前的图书查找举例:pb.url为

1
/mall/BookServlet?method=findByCategory&cid=5F79D0D246AD4216AC04E9C5FAB3199E

假设当前页码为3,那么pb.pc就为3;
那么这个url就是:

1
/mall/BookServlet?method=findByCategory&cid=5F79D0D246AD4216AC04E9C5FAB3199E&pc=2

通过这个url,我们可以知道是进入了servlet层的findByCategory方法,并且传递了2个参数cid和pc;接下来我将贴出findByCategory的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
* 按分类查询
* @param req
* @param resp
* @return
* @throws ServletException
* @throws IOException
*/
public String findByCategory(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
/*
* 1.得到pc:如果页面传,使用页面的;
* 如果没传,pc=1;
*/
int pc = getPc(req);
/*
* 2.得到url
*/
String url = getUrl(req);
/*
* 3.获取查询条件,本方法是cid,即分类的id
*/
String cid = req.getParameter("cid");
/*
* 4.使用pc和cid调用的service#findByCategory得到PageBean
*/
PageBean<Book> pb = bookService.findByCategory(cid, pc);
/*
* 5.给PageBean设置url,保存PageBean,转发到/jsps/book/list.jsp
*/
pb.setUrl(url);
req.setAttribute("pb", pb);
return "f:/jsps/book/list.jsp";
}
/**
* 获取当前页码
* @param req
* @return
*/
private int getPc(HttpServletRequest req){
int pc = 1;
String param = req.getParameter("pc");
if(param!=null&&!param.trim().isEmpty()){//如果从页面得到的pc不为null,并且不为空格
try{
pc = Integer.parseInt(param);
}catch(RuntimeException e){
}
}
return pc;
}
/**
* 截取url,页面中的分页导航中需要使用它作为超链接的目标!
* @param req
* @return
*/
/*
* 比如说请求地址:
* http://localhost:8080/mall/BookServlet?method=findByCategory&cid=xxx
* 获取的是:/mall/BookServlet + method=findByCategory&cid=xxx
*/
private String getUrl(HttpServletRequest req){
//req.getQueryString()得到的是?后面的字符串
String url = req.getRequestURI() + "?" +req.getQueryString();
/*
* 如果url中存在pc参数,截取掉,如果不存在那就不用截取。
*/
int index = url.lastIndexOf("&pc=");
if(index!=-1){
url = url.substring(0, index);
}
return url;
}

得到相应的service层(BookService)方法代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 按分类查询
* @param cid
* @param pc
* @return
*/
public PageBean<Book> findByCategory(String cid,int pc){
try {
return bookDao.findByCategory(cid, pc);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}

得到相应的Dao层(BookDao)方法代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
* 按分类查询
* @param cid
* @param pc
* @return
* @throws SQLException
*/
public PageBean<Book> findByCategory(String cid,int pc) throws SQLException{
List<Expression> exprList = new ArrayList<Expression>();
exprList.add(new Expression("cid","=",cid));
return findByCriteria(exprList, pc);
}
/**
* 通用的查询方法
* @param exprList
* @param pc
* @return
* @throws SQLException
*/
private PageBean<Book> findByCriteria(List<Expression> exprList,int pc) throws SQLException{
/*
* 1.得到ps
* 2.得到tr
* 3.得到beanList
* 4.创建PageBean,返回
*/
/*
* 1.得到ps
*/
int ps = PageConstants.BOOK_PAGE_SIZE;//每页记录数
/*
* 2.通过exprList生成where字句
*/
/*
* 这里是因为where后面的条件个数不好控制,
* 先写一个where1=1,对后面的语句没有影响,然后后面的都以and开头
*/
StringBuilder whereSql = new StringBuilder("where 1=1");
List<Object> params = new ArrayList<Object>();//SQL中有问号,它是对应问号的值。
for(Expression expression:exprList){
/*
* 添加一个条件
* 1.以and开头
* 2.条件的名称
* 3.条件的运算符,可以是=、!=、>、<...is null;is null 没有值
* 4.如果条件不是is null,再追加问号,然后再向params中添加一个与问号对应的值
*/
whereSql.append(" and ").append(expression.getName()).append(" ")
.append(expression.getOperator()).append(" ");
if(!expression.getOperator().equals("is null")){
whereSql.append("?");
params.add(expression.getValue());
}
}
/*
* 3.总记录数
* select count(*) from t_book where
*/
String sql = "select count(*) from t_book " + whereSql;
Number number = (Number)qr.query(sql,new ScalarHandler(),params.toArray());
int tr = number.intValue();// 得到了总记录数
/*
* 4.得到beanList,即当前页记录
*/
sql = "select * from t_book " + whereSql + " order by orderBy limit ?,?";
params.add((pc-1)*ps);//第一个问号,当前页首行记录的下标
params.add(ps);//第二个问号
List<Book> beanList =
qr.query(sql,new BeanListHandler<Book>(Book.class),params.toArray());
/*
* 5.创建PageBean,设置参数
* 其中PageBean没有url,由servlet完成
*/
PageBean<Book> pb = new PageBean<Book>();
pb.setBeanList(beanList);
pb.setPc(pc);
pb.setTr(tr);
pb.setPs(ps);
return pb;
}

这里得到的PageBean对象pb包含了一个pc(当前页码)、ps(每页记录数)、tr(总记录数)、beanList(List当前页的记录);
其中url是没有设置的;在servlet层会得到这个pb对象,然后再设置url,传到JSP,JSP又根据这个url在后端执行代码。这样整个pageBean就做好了。

第二种

第二种分页技术是我在一个OA系统项目(用了SSH框架)上用的,对比上面那种有所不同。贴上代码:

pageBean类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package yh.oa.domain;
import java.util.List;
/**
* 分页功能中的一页的信息
*
* @author tyg
*
*/
public class PageBean {
// 指定的或是页面参数
private int currentPage; // 当前页
private int pageSize; // 每页显示多少条
// 查询数据库
private int recordCount; // 总记录数
private List recordList; // 本页的数据列表
// 计算
private int pageCount; // 总页数
private int beginPageIndex; // 页码列表的开始索引(包含)
private int endPageIndex; // 页码列表的结束索引(包含)
/**
* 只接受前4个必要的属性,会自动的计算出其他3个属性的值
*
* @param currentPage
* @param pageSize
* @param recordCount
* @param recordList
*/
public PageBean(int currentPage, int pageSize, int recordCount, List recordList) {
this.currentPage = currentPage;
this.pageSize = pageSize;
this.recordCount = recordCount;
this.recordList = recordList;
// 计算总页码
pageCount = (recordCount + pageSize - 1) / pageSize;
// 计算 beginPageIndex 和 endPageIndex
// >> 总页数不多于10页,则全部显示
if (pageCount <= 10) {
beginPageIndex = 1;
endPageIndex = pageCount;
}
// >> 总页数多于10页,则显示当前页附近的共10个页码
else {
// 当前页附近的共10个页码(前4个 + 当前页 + 后5个)
beginPageIndex = currentPage - 4;
endPageIndex = currentPage + 5;
// 当前面的页码不足4个时,则显示前10个页码
if (beginPageIndex < 1) {
beginPageIndex = 1;
endPageIndex = 10;
}
// 当后面的页码不足5个时,则显示后10个页码
if (endPageIndex > pageCount) {
endPageIndex = pageCount;
beginPageIndex = pageCount - 10 + 1;
}
}
}
public List getRecordList() {
return recordList;
}
public void setRecordList(List recordList) {
this.recordList = recordList;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getPageCount() {
return pageCount;
}
public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getRecordCount() {
return recordCount;
}
public void setRecordCount(int recordCount) {
this.recordCount = recordCount;
}
public int getBeginPageIndex() {
return beginPageIndex;
}
public void setBeginPageIndex(int beginPageIndex) {
this.beginPageIndex = beginPageIndex;
}
public int getEndPageIndex() {
return endPageIndex;
}
public void setEndPageIndex(int endPageIndex) {
this.endPageIndex = endPageIndex;
}
}

pageView.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<div id=PageSelectorBar>
<div id=PageSelectorMemo>
页次:${currentPage}/${pageCount }页 &nbsp;
每页显示:${pageSize }条 &nbsp;
总记录数:${recordCount }条
</div>
<div id=PageSelectorSelectorArea>
<a href="javascript: gotoPage(1)" title="首页" style="cursor: hand;">
<img src="${pageContext.request.contextPath}/style/blue/images/pageSelector/firstPage.png"/>
</a>
<s:iterator begin="%{beginPageIndex}" end="%{endPageIndex}" var="num">
<s:if test="#num == currentPage"> <%-- 当前页 --%>
<span class="PageSelectorNum PageSelectorSelected">${num}</span>
</s:if>
<s:else> <%-- 非当前页 --%>
<span class="PageSelectorNum" style="cursor: hand;" onClick="gotoPage(${num});">${num}</span>
</s:else>
</s:iterator>
<a href="javascript: gotoPage(${pageCount})" title="尾页" style="cursor: hand;">
<img src="${pageContext.request.contextPath}/style/blue/images/pageSelector/lastPage.png"/>
</a>
转到:
<select onchange="gotoPage(this.value)" id="_pn">
<s:iterator begin="1" end="%{pageCount}" var="num">
<option value="${num}">${num}</option>
</s:iterator>
</select>
<script type="text/javascript">
$("#_pn").val("${currentPage}");
</script>
</div>
</div>
<script type="text/javascript">
function gotoPage( pageNum ){
$(document.forms[0]).append("<input type='hidden' name='pageNum' value='" + pageNum +"'>");
document.forms[0].submit();
}
</script>

JSP页面效果展示:

这里的数据是从pageBean得到的。


这里的页码,首页,尾页和转到第几页都可以点,并跳到相应的页码,分页技术的重点也在这里。
pageView.jsp的代码也主要说下页码,首页,尾页和转到第几页的url;按页码举例:

1
onClick="gotoPage(${num});

所以整个分页技术的核心在gotoPage(${num})这个function上。
再来贴一下这个function:

1
2
3
4
function gotoPage( pageNum ){
$(document.forms[0]).append("<input type='hidden' name='pageNum' value='" + pageNum +"'>");
document.forms[0].submit();
}

分析一下,第2行代码增加了一个隐藏的输入框,name是pageNum;value是页码(数字);
第3行代码做了一个提交的操作。注意:pageView.jsp中并没有form表单,因为它只是一个块,真正使用是在show.jsp中,
show.jsp也列出了每页的信息;下面贴出部分show.jsp的代码:

1
2
3
4
5
6
<s:form action="forum_show?id=%{id}">
//略
</s:form>
<!--分页信息-->
<%@ include file="/WEB-INF/jsp/public/pageView.jspf" %>

可以知道document.forms[0].submit();执行之后,提交了表单,并且加了一个值为页码的参数pageNum;
接下来看struts.xml代码:

1
2
3
4
<action name="forum_*" class="forumAction" method="{1}">
<result name="list">/WEB-INF/jsp/forumAction/list.jsp</result>
<result name="show">/WEB-INF/jsp/forumAction/show.jsp</result>
</action>

可以知道上面表单提交之后其实是去了forumAction的show方法;
看servlet代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/** 显示单个版块(主题列表) */
public String show() throws Exception {
// 准备数据:forum
Forum forum = forumService.getById(model.getId());
ActionContext.getContext().put("forum", forum);
// 准备分页信息
new QueryHelper(Topic.class, "t")//
// 过滤条件
.addCondition("t.forum=?", forum)//
.addCondition((viewType == 1), "t.type=?", Topic.TYPE_BEST) // 1 表示只看精华帖
// 排序条件
.addOrderProperty((orderBy == 1), "t.lastUpdateTime", asc) // 1 表示只按最后更新时间排序
.addOrderProperty((orderBy == 2), "t.postTime", asc) // 2 表示只按主题发表时间排序
.addOrderProperty((orderBy == 3), "t.replyCount", asc) // 3 表示只按回复数量排序
.addOrderProperty((orderBy == 0), "(CASE t.type WHEN 2 THEN 2 ELSE 0 END)", false)//
.addOrderProperty((orderBy == 0), "t.lastUpdateTime", false) // 0 表示默认排序(所有置顶帖在前面,并按最后更新时间降序排列)
.preparePageBean(topicService, pageNum, pageSize);
return "show";
}

这个代码中其实只有preparePageBean(topicService, pageNum, pageSize)与分页有关,不过为了便于对show方法的理解,我将贴上QueryHelper类的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package yh.oa.util;
import java.util.ArrayList;
import java.util.List;
import yh.oa.base.DaoSupport;
import yh.oa.domain.PageBean;
import com.opensymphony.xwork2.ActionContext;
/**
* 用于辅助拼接HQL语句
*
* @author tyg
*
*/
public class QueryHelper {
private String fromClause; // FROM子句
private String whereClause = ""; // Where子句
private String orderByClause = ""; // OrderBy子句
private List<Object> parameters = new ArrayList<Object>(); // 参数列表
/**
* 生成From子句
*
* @param clazz
* @param alias
* 别名
*/
public QueryHelper(Class clazz, String alias) {
fromClause = "FROM " + clazz.getSimpleName() + " " + alias;
}
/**
* 拼接Where子句
*
* @param condition
* @param params
*/
public QueryHelper addCondition(String condition, Object... params) {
// 拼接
if (whereClause.length() == 0) {
whereClause = " WHERE " + condition;
} else {
whereClause += " AND " + condition;
}
// 参数
if (params != null) {
for (Object p : params) {
parameters.add(p);
}
}
return this;
}
/**
* 如果第一个参数为true,则拼接Where子句
*
* @param append
* @param condition
* @param params
*/
public QueryHelper addCondition(boolean append, String condition, Object... params) {
if (append) {
addCondition(condition, params);
}
return this;
}
/**
* 拼接OrderBy子句
*
* @param propertyName
* 参与排序的属性名
* @param asc
* true表示升序,false表示降序
*/
public QueryHelper addOrderProperty(String propertyName, boolean asc) {
if (orderByClause.length() == 0) {
orderByClause = " ORDER BY " + propertyName + (asc ? " ASC" : " DESC");
} else {
orderByClause += ", " + propertyName + (asc ? " ASC" : " DESC");
}
return this;
}
/**
* 如果第一个参数为true,则拼接OrderBy子句
*
* @param append
* @param propertyName
* @param asc
*/
public QueryHelper addOrderProperty(boolean append, String propertyName, boolean asc) {
if (append) {
addOrderProperty(propertyName, asc);
}
return this;
}
/**
* 获取生成的用于查询数据列表的HQL语句
*
* @return
*/
public String getListQueryHql() {
return fromClause + whereClause + orderByClause;
}
/**
* 获取生成的用于查询总记录数的HQL语句
*
* @return
*/
public String getCountQueryHql() {
return "SELECT COUNT(*) " + fromClause + whereClause;
}
/**
* 获取HQL中的参数值列表
*
* @return
*/
public List<Object> getParameters() {
return parameters;
}
/**
* 查询分页信息,并放到值栈栈顶
*
* @param service
* @param pageNum
* @param pageSize
*/
public void preparePageBean(DaoSupport<?> service, int pageNum, int pageSize) {
PageBean pageBean = service.getPageBean(pageNum, pageSize, this);
ActionContext.getContext().getValueStack().push(pageBean);
}
}

与分页有关的方法是QueryHelper的最后一个方法preparePageBean,这个方法传了3个参数进去:service,页码,每页条数;
因为service参数传的是对象topicService。可以知道是从topicService对象中调用getPageBean方法。
贴上TopicServiceImpl类中的getPageBean方法代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public PageBean getPageBeanByForum(int pageNum, int pageSize, Forum forum) {
// 查询本页的数据列表
List list = getSession().createQuery(//
"FROM Topic t WHERE t.forum=? ORDER BY (CASE t.type WHEN 2 THEN 2 ELSE 0 END) DESC, t.lastUpdateTime DESC")//
.setParameter(0, forum)//
.setFirstResult((pageNum - 1) * pageSize)//
.setMaxResults(pageSize)//
.list();
// 查询总记录数量
Long count = (Long) getSession().createQuery(//
"SELECT COUNT(*) FROM Topic t WHERE t.forum=?")//
.setParameter(0, forum)//
.uniqueResult();
return new PageBean(pageNum, pageSize, count.intValue(), list);
}

这个方法执行完,会返回一个知道了当前页码,每页显示多少条,总记录数以及本页的数据列表的PageBean对象;
而这4个属性知道后,可以自动计算出后面的3个属性:总页数,页码的开始索引,结束索引。
最终这个PageBean对象会被传递到JSP页面,显示效果。

总结

比较上述两种方法:
PageBean相同点:两个PageBean类都包含当前页码、每页记录数、总记录数、本页的数据列表四个属性。总页码根据总记录数和每页记录数得到()。

PageBean不同点(第一种PageBean简述为P1,第二种PageBean简述为P2):P1多了属性url,在JSP中直接点击访问servlet方法获取PageBean再返回到JSP。P2没有这个url是因为点击页码的时候进行的是表单提交的操作,而对应要访问的方法写在了form的action中。P2比P1多了2个属性:页码列表的开始索引和结束索引,是因为第一种方法将这个计算放在了JSP中进行。
细节方面这里就不详细说了,个人觉得我第一种方法比较好,比较灵活,因为还用到了PageConstants类和Expression类;常量的设置比较便捷,并且能够根据各种条件查找相应的数据。第一种方法中P1中用了url的方式和第二种方法用表单提交的方式,个人感觉其实是差不多的,只是侧重的点不同罢了。单单从分页技术来看,我跟倾向于第一种方式,虽然可能复杂了点,但是使用起来更灵活,对于后期的维护和功能的修改是更有好处的。当然他们也有可以互相结合的地方,只是我当时没有想到而已。比如P1中可以多出页码列表的开始索引和结束索引,这样JSP页面就可以不必写的那么麻烦了。

最后:在写代码的时候多做思考,一个会思考的程序员才能更快地成长!

「真诚赞赏,手留余香」