在ITEye上看了一些查询分页实现,实在不咋的。说说自己的实现,供大家参考。
思路:
1. 使用自定义标签来展示分页并完成分页参数的传递
2. 采用Struts的Interceptor拦截分页参数,保存至ThreadLocal变量。注意每个http request处理完成后一定要清除ThreadLocal。不是用struts的项目可采用Filter等替代此struts拦截器
3. 在Dao层最后执行查询的Query中,去ThreadLocal检查是否有分页参数,即是否需要分页。action, service, 完全不用传递分页参数
====================================================================
最终调用方式举例:
步骤1:
Http Get方式:把此代码插入需要显示分页的地方<app:page href='getUserList.do' /> 。请写明分页请求路径。如:getUserList.do
Http Post方式:把此代码插入需要显示分页的地方<app:page form='useListForm' /> 。请写明提交表单的form的id
步骤2:
Dao层中方法:List results = query.list() 改写成:List result = super.list(query)。在super.list(query)中处理分页查询
只需要如上两部即完成分页的所有过程
====================================================================
伪代码实现:
Struts拦截器(不是struts框架可采用Filter实现)
public class PageInterceptor extends AbstractInterceptor {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public String intercept(ActionInvocation invocation) throws Exception {
String result = null;
try {
HttpServletRequest request = ServletActionContext.getRequest();
Integer currentPage = getIntegerParameter(request, "page");
// 是否需要分页, 是则保存参数到ThreadLocal
if (currentPage != null) {
Page page = new Page();
page.setCurrentPage(currentPage);
ThreadLocalData.setPage(page);
}
result = invocation.invoke();
} finally {
// 重要: 必须清除此值,否则线程池会拿到错误分页数据
ThreadLocalData.removePage();
}
return result;
}
private Integer getIntegerParameter(HttpServletRequest request, String name) {
String value = request.getParameter(name);
if (value == null) {
return null;
} else {
return Integer.parseInt(value.trim());
}
}
}
存放分页参数的ThreadLocal
public class ThreadLocalData {
private static final ThreadLocal<Page> PAGE_THREAD_LOCAL = new ThreadLocal<Page>();
public static Page getPage() {
return PAGE_THREAD_LOCAL.get();
}
public static void setPage(Page page) {
PAGE_THREAD_LOCAL.set(page);
}
public static void removePage() {
PAGE_THREAD_LOCAL.remove();
}
}
BaseDao中填写分页查询函数
public class BaseDao {
private Session session;
/**
* 分页查询函数。会检查是否有分页参数,如果有则执行分页查询,否则执行普通查询
* @param queryString
* @return
*/
public List list(Query query) {
// 检查是否需要分页, 并设置到query对象中
Page page = ThreadLocalData.getPage();
if (page != null) {
// 如果需要查询总记录数,执行下面语句。否则跳过
String queryString = query.getQueryString();
int fromIndex = queryString.toLowerCase().indexOf("from");
Query countQuery = getSession().createQuery("select count(*) " + queryString.substring(fromIndex));
Integer totalCount = Integer.parseInt(countQuery.uniqueResult().toString());
// 分页查询
int currentPage = page.getCurrentPage();
int pageSize = page.getPageSize();
query.setFirstResult((currentPage - 1) * pageSize);
query.setMaxResults(pageSize);
// 计算总页数
page.setTotalPage(totalCount / pageSize + totalCount % pageSize == 0 ? 0 : 1);
// 重新写入ThreadLocal
ThreadLocalData.setPage(page);
}
List results = query.list();
return results;
}
具体的Dao实现
public class UserDao extends BaseDao {
@SuppressWarnings("unchecked")
public List<User> getUserDao() {
String queryString = "from User where age > ?";
Query query = getSession().createQuery(queryString);
query.setParameter(1, 20);
// 唯一不爽的地方: 不能写query.list()。需要用super.list(query)替代
return super.list(query);
}
}
分页标签
public class PageTag extends TagSupport {
/**
*
*/
private static final long serialVersionUID = -4863513329378411411L;
// HttpGet方式分页,传递分页请求路径
private String href;
// HttpPost方式分页,传递jsp页面中form id
private String form;
@Override
public int doStartTag() throws JspException {
JspWriter out = pageContext.getOut();
// 从ThreadLocal中取出分页参数
Page page = ThreadLocalData.getPage();
if (page == null)
return super.doStartTag();
try {
if (StringUtils.isBlank(form)) {
// HttpGet方式分页。大致代码如下
// class='style'为交给美工的样式
out.print("<div class='style'>");
out.print("<a href='" + href + "?page=" + (page.getCurrentPage() - 1) + "'>上一页</a> <a>当前页</a> <a href='...'>下一页</a>");
out.print("</div>");
} else {
// HttpPost方式分页。大致代码如下
// class='style'为交给美工的样式
out.print("<div class='style'>");
out.print("<a href='javascript:linkTo(" + (page.getCurrentPage() - 1) + "'>上一页</a> <a>当前页</a> <a href='...'>下一页</a>");
out.print("</div>");
// 继续输出一段分页用的js脚本。此脚本用了jquery写法
StringBuffer sb = new StringBuffer();
sb.append("<script type=\"text/javascript\">");
sb.append("function linkTo(curPos) {");
sb.append("if (curPos == '') { return ; } ");
sb.append("var frm = $(\"#" + getForm() + "\");");
// 追加分页参数到form表单
sb.append("$(frm).append(\"<input type='hidden' name='page' value='\"+curPos+\"' />\");");
sb.append("$(frm).submit();}</script>");
out.print(sb.toString());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return super.doStartTag();
}
// //
// Getter Setter 略
}
分页标签page.tld
<taglib>
<tlibversion>1.2</tlibversion>
<jspversion>1.1</jspversion>
<shortname>PageTag</shortname>
<uri>PageTag</uri>
<tag>
<name>page</name>
<tagclass>com.xxx.PageTag</tagclass>
<attribute>
<name>href</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>form</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
唯一不爽的地方UserDao需要调用super.list(query), 尝试过继承Hibernate Query对象,复写query.list()方法,在自己的list方法中写分页相关逻辑,没成功。有实现了的同志不妨指点指点。