引言
分页是我们系统中常用的一个功能,尤其是在列表展示页面。下面总结一下SSH中分页功能的封装。
第一、封装分页实体PageBean
package cn.itcast.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;
}
}
第二、封装工具类(用来拼接hql语句)
package cn.itcast.oa.until;
import java.util.ArrayList;
import java.util.List;
import cn.itcast.oa.base.dao.DaoSupport;
import cn.itcast.oa.domain.PageBean;
import com.opensymphony.xwork2.ActionContext;
/***
* 用户拼接HQL语句
*
* @author zhenghao
*
*/
public class QueryHelper {
private String fromClause; // FROM子句
private String whereClause = ""; // Where子句
private String orderByClause = ""; // OrderBy子句
private List<Object> parameters = new ArrayList<Object>();// 参数列表
/***
* 生成From字句
*/
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);
}
}
方法解读:
有一个构造方法,参数为类名和别名,其中拼接了一个from字句,这是因为每一个查询的hql语句from字句是必须有的,所以from字句的拼接在构造方法里面就完成了。
有两个拼接条件的方法addCondition,主要是完成where字句的拼接,并且每拼接一个查询约束条件,对应在参数列表中添加一个参数,参数列表中的参数位置和where中的?的位置匹配。
有两个拼接排序条件的方法,主要是完成order by字句的拼接,参数为排序的字段 和 排序规则(ASC OR DESC),之所以是两个,是因为其中一个多一个用来判断的boolean类型的参数。
有一个获取生成的用于查询数据列表的HQL语句方法getListQueryHql()
有一个获取生成的用于查询总记录数的HQL语句getCountQueryHql()
有一个获取hql中参数列表的方法getParameters()
还有一个将查询的结果放在值栈中的方法preparePageBean()
总结:有了这样一个工具类以后,我们在action中只需要写一句代码就可以完成,分页的调用。需要主要是,上面工具类中,有很多的方法都是直接返回“this”。
第三步、封装service接口(在service中直接操作了数据库)父类
/**
* 公共的查询分页信息的方法(最终版)
*
* @param pageNum
* @param pageSize
* @param queryHelper
* HQL语句与参数列表
* @return
*/
PageBean getPageBean(int pageNum, int pageSize, QueryHelper queryHelper);
第四步:具体的service中的实现()
// 公共的查询分页信息的方法(最终版)
public PageBean getPageBean(int pageNum, int pageSize, QueryHelper queryHelper) {
System.out.println("-------> DaoSupportImpl.getPageBean( int pageNum, int pageSize, QueryHelper queryHelper )");
// 参数列表
List<Object> parameters = queryHelper.getParameters();
// 查询本页的数据列表
Query listQuery = getSession().createQuery(queryHelper.getListQueryHql()); // 创建查询对象
if (parameters != null) { // 设置参数
for (int i = 0; i < parameters.size(); i++) {
listQuery.setParameter(i, parameters.get(i));
}
}
listQuery.setFirstResult((pageNum - 1) * pageSize);
listQuery.setMaxResults(pageSize);
List list = listQuery.list(); // 执行查询
// 查询总记录数量
Query countQuery = getSession().createQuery(queryHelper.getCountQueryHql());
if (parameters != null) { // 设置参数
for (int i = 0; i < parameters.size(); i++) {
countQuery.setParameter(i, parameters.get(i));
}
}
Long count = (Long) countQuery.uniqueResult(); // 执行查询
return new PageBean(pageNum, pageSize, count.intValue(), list);
}
第五步、具体的action的实现(topic)
// 分页信息最终版
new QueryHelper(Reply.class, "r")//
.addCondition("r.topic=?", topic)//
.addOrderProperty("r.postTime", true)//
.preparePageBean(replyService, pageNum, pageSize);
带有过滤条件的分页中的action实现
// 准备分页信息 ,最终版
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);
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<div id=PageSelectorBar>
<div id=PageSelectorMemo>
页次:${currentPage}/${pageCount }页
每页显示:${pageSize }条
总记录数:${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 οnchange="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 ){
// window.location.href = "forum_show.action?id=${id}&pageNum=" + pageNum;
$(document.forms[0]).append("<input type='hidden' name='pageNum' value='" + pageNum +"'>");
document.forms[0].submit();
}
</script>
第七步、在具体页面中使用,在需要使用分页的地方写入下面代码
<!--分页信息-->
<%@ include file="/WEB-INF/jsp/public/pageView.jspf"%>
<s:form action="topic_show?id=%{id}"></s:form>
如果我们需要多个筛选条件的话,可以将form表单将需要的条件包含住即可
第八步、效果
小结
通过这样封装以后,如果在其他的页面中用到分页的话,将会很快的完成,并且会大大降低出错的几率,这也是封装的一个很大的优势!以后有类似的功能,则可以信手拈来啦!!!