分页设计思路:
JSP中与分页相关的信息
l 要显示本页的数据列表
• 使用<s:iterator value=“%{recordList}”>循环显示
l 要显示分页信息,包括:
• 当前页码、总页码数、每页显示多少条、总记录数
• 页码列表,只显示当前页附近的10个页码
•
|
快速转到功能:使用下拉列表显示所有的页码,选中某页码后就转到相应的页面
使用Hibernate获取分页的数据
l 在Hibernate中获取一段数据的方式是:
• Query.setFirstResult(int)
• Query.setMaxResults(int)
举例:假设共有25条数据,每页显示10条(pageSize=10),则总共3页,获取每页数据的firstResult与maxResult分别为:
• 第1页:first=0,max=10
• 第2页:first=10,max=10
• 第3页:first=20,max=10
l 公式说明:
• 计算firstResult的公式为:(当前页-1) * pageSize
• maxResults就是pageSize的值
总页数及页码列表的开始与结束索引的计算方法
l 计算总页码数pageCount的方式为:
• 方式1:recordCount / pageSize,如果有余数,就再加1页
• 方式2:(recordCount + pageSize - 1) / pageSize
l 计算beginPageIndex与endPageIndex(最多显示当前页附近的10个页码)
• 总页数小于等于10页,则全部显示
• 总页数大于10页
• 默认显示当前页附件近的10个页码(前4个 + 当前页 + 后5个)
• 前面不足4个页码时,就显示前10个页码
• 后面不足5个页码时,就显示后10个页码
一、 基本分页
topicAction/show.jsp =========分页的信息 ========== 页次:${currentPage}/{pageCount}页 每页显示:${pageSize}条 总记录数:${recordCount}条 <s:iteratorbegin="%{beginPageIndex}" end="%{endPageIndex}"var="num"> ${num} </s:iterator>
转到: <select id="pn"οnchange="gotoPage(this.value)"> <s:iteratorbegin="1" end="%{pageCount}" var="num"> <optionvalue="${num}">${num}</option> </s:iterator> </select>
<script> functiongotoPage( pageNum ){ window.location.href="xxx.action?pageNum=" + pageNum; } </script> | TopicAction中
/** 显示单个主题(主帖 +回帖列表) */ public String show()throws Exception { //准备数据 //Topic Topic topic=topicService .getById(model.getId());
ActionContext.getContext() .put("topic", topic);
PageBean pageBean=replyService .getPageBean(pageNum,topic);
ActionContext.getContext() .getValueStack().push(pageBean); return "show"; } | ReplyServiceImpl中
public PageBean getPageBean(int pageNum, Topic topic){
int pageSize=Configuration.getPageSize(); //查询本页的数据列表 List list=getSession().createQuery(// "FROM Reply r WHERE r.topic=? ORDER BY r.postTime") .setParameter(0, topic) .setFirstResult((pageNum-1)*pageSize) .setMaxResults(pageSize) .list();
// 查询总记录数 Long recordCount=(Long) getSession() .createQuery(// "SELECT count(*) FROM Reply r WHERE r.topic=?") .setParameter(0, topic) .uniqueResult();
return new PageBean(pageNum, pageSize, recordCount.intValue(), list); }
|
各种数据列表查询,不同的只是HQL与其中的参数列表,所以把这两个信息作为参数传到公共方法中就可以了,其中HQL
只需要传递“查询数据列表”的就行,要想查询总记录数,直接在这个HQL前面加上“select count(*)”就可以得到查询总数量的HQL语句。所以,设计的方法如下:
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{
// 默认显示前4页 +当前页 + 后5页
beginPageIndex=currentPage-4;
endPageIndex=currentPage+5;
// 如果前面不足4个页码时,则显示前10页
if(beginPageIndex<1){
beginPageIndex=1;
endPageIndex=10;
}
// 如果后面不足5个页码时,则显示后10页
else if(endPageIndex>pageCount){
endPageIndex=pageCount;
beginPageIndex=endPageIndex-9;
}
}
}
ForumAction中:
//准备主题列表的分页信息(使用公共的方法 +过滤与排序)
String hql="FROM Topic t WHERE t.forum=?";
List<Object> parameters= new ArrayList<Object>();
parameters.add(forum);
// >>过滤条件
if(viewType== 1){ // 1表示只看精华帖
hql += " AND t.type=? ";
parameters.add(Topic.TYPE_BEST);
}
// >> 排序条件
if(orderBy== 0){ //默认排序
hql+="ORDER BY (CASE t.type WHEN 2 THEN 2 ELSE0 END) DESC, t.lastUpdateTime DESC";
}
else if(orderBy ==1){ //1 代表只按最后更新时间排序<br>
hql+="ORDER BY t.lastUpdateTime "+(asc? "ASC":"DESC");
}
else if(orderBy ==2){ // 2 代表只按主题发表时间排序<br>
hql+="ORDER BY t.postTime "+(asc? "ASC":"DESC");
}
else if(orderBy ==3){ // 3 代表只按回复数量排序
hql+="ORDER BY t.replyCount "+(asc? "ASC":"DESC");
}
PageBean pageBean=topicService.getPageBean(pageNum, hql, parameters.toArray());
ActionContext.getContext().getValueStack().push(pageBean);
-----------------------------------------------------------------------------------------------------------------------------------------------------
BaseDaoImpl中:
public PageBean getPageBean(int pageNum, String queryListSQL,Object[] parameters) {
int pageSize=Configuration.getPageSize();
//查询本页的数据列表
Query listQuery=getSession().createQuery(queryListSQL);
if(parameters!= null && parameters.length>0){
for(int i=0;i<parameters.length;i++){
listQuery.setParameter(i, parameters[i]);
}
}
listQuery.setFirstResult((pageNum-1)*pageSize);
listQuery.setMaxResults(pageSize);
List list=listQuery.list();
// 查询总记录数
Query countQuery=getSession().createQuery("SELECT count(*) "+queryListSQL);
if(parameters!= null && parameters.length>0){
for(int i=0;i<parameters.length;i++){
countQuery.setParameter(i, parameters[i]);
}
}
Long recordCount=(Long) countQuery.uniqueResult();
return new PageBean(pageNum, pageSize, recordCount.intValue(), list);
}
===========================================================================================
三、优化
如果有一个辅助生成HQL的工具类HqlHelper(后面页有详细说明),他的作用是持有HQL与参数列表。则这个获取分页信息的公共方法可以设计为:
其中:
HqlHelper.getQueryListHql() 获取生成的查询数据列表的HQL
HqlHelper.getQueryCountHql() 获取生成的查询总数量的HQL(没有OrderBy子句)
HqlHelper.getParameters() 获取参数列表
l 我们所用的查询数据列表的HQL一般的格式如下:
“FROM 实体 WHERE条件1 AND 条件2 AND ... AND 条件n ORDER BY 属性1, 属性2, ..., 属性n”
l 说明:
• From子句必须要有
• Where子句是可选的
• OrderBy子句是可选的
l 说明2:
• Where子句中的多个条件之间是AND的关系,因为使用的场景都是数据列表的过滤,例如用户列表可以按姓名、性别、生日范围等条件过滤,如果指定了多个条件,这多个条件之间都是AND的关系。
• Where子句中的表达式中可能0或多个参数:
• 没有参数的例子:t.forum IS NULL
• 1个参数的例子:t.id=?
• 2个参数的例子:t.id BETWEEN ? AND ?
•
HqlHelper的设计:
public class HqlHelper {
private String fromClause;
private String whereClause="";
private String orderByClause="";
private List<Object> parameters=new ArrayList<Object>();
/**
* 生成From字句,默认别名为"o"
* @param clazz
*/
public HqlHelper(Class clazz){
this.fromClause="FROM "+ clazz.getSimpleName()+ " o";
}
/**
* 生成From字句,默认别名为"o"
* @param clazz
*/
public HqlHelper(Class clazz,String alias){
this.fromClause="FROM "+ clazz.getSimpleName()+ " "+alias;
}
/**
* 拼接where字句
* @param condition
* @param params
* @return
*/
public HqlHelper addCondition(String condition,Object...params){
//拼接
if(whereClause.length()==0){
whereClause=" WHERE "+ condition;
}else{
whereClause+=" AND "+ condition;
}
// 保存参数
if(params!= null && params.length>0){
for(Object obj:params){
parameters.add(obj);
}
}
return this;
}
/**
* 如果第1个参数为true,则拼接Where子句
*
* @param append
* @param condition
* @param params
* @return
*/
public HqlHelper addCondition(boolean append,String condition,Object...params){
if(append){
addCondition(condition, params);
}
return this;
}
/**
* 拼接order by字句
* @param propertyName
* @param isAsc
* @return
*/
public HqlHelper addOrder(StringpropertyName,boolean isAsc){
if(orderByClause.length()== 0){
orderByClause=" ORDER BY "+propertyName+ (isAsc? " ASC":" DESC");
}else{
orderByClause+=", "+propertyName+(isAsc? " ASC":" DESC");
}
return this;
}
/**
* 拼接order by字句
* @param propertyName
* @param isAsc
* @return
*/
public HqlHelper addOrder(boolean append,String propertyName,boolean isAsc){
if(append){
addOrder(propertyName, isAsc);
}
return this;
}
/**
* 获取生成的查询数据列表的HQL语句
* @return
*/
public String getQueryListHql(){
return fromClause+ whereClause + orderByClause;
}
/**
* 获取生成的查询数据列表的HQL语句
* @return
*/
public String getQueryCountHql(){
return "SELECT count(*) "+fromClause+ whereClause;
}
/**
* 获取参数列表,与HQL过滤条件中的'?'一一对应
* @return
*/
public List<Object> getParameters(){
return parameters;
}
public HqlHelper buildPageBeanForStruts2(int pageNum, BaseDao<?> service){
System.out.println("===>HqlHelper.buildPageBeanForStruts2()");
PageBean pageBean = service.getPageBean(pageNum,this);
ActionContext.getContext().getValueStack().push(pageBean);
return this;
}
}
在ForumAction中“:
//========================================(最终版)==========================================
// 最终版:
// 构建查询条件
new HqlHelper(Topic.class,"t")//
.addCondition("t.forum=?", forum)//
.addCondition(viewType== 1,"t.type=?", Topic.TYPE_BEST)// 1表示只看精华帖
.addOrder(orderBy== 1,"t.lastUpdateTime", asc)// 1 代表只按最后更新时间排序
.addOrder(orderBy== 2,"t.postTime", asc)// 2 代表只按主题发表时间排序
.addOrder(orderBy== 3,"t.replyCount", asc)// 3 代表只按回复数量排序
.addOrder(orderBy== 0,"(CASE t.type WHEN 2 THEN 2 ELSE 0END)",false)//
.addOrder(orderBy== 0,"t.lastUpdateTime",false) // 0 代表默认排序(所有置顶帖在前面,并按最后更新时间降序排列)
.buildPageBeanForStruts2(pageNum, replyService);
return "show";