在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方法中写分页相关逻辑,没成功。有实现了的同志不妨指点指点。
分享到:
相关推荐
ThreadLocal的工作原理主要基于以下几点: - **内部类ThreadLocalMap**:ThreadLocal在每个线程内部维护了一个名为ThreadLocalMap的哈希映射表,这个表的键是ThreadLocal对象,值是线程局部变量的实际值。这样,每...
ThreadLocal是一种在多线程环境下为每一个线程提供各自变量副本的机制,以确保线程安全。在Java中,ThreadLocal被广泛应用于Web中间件、服务端编程和微服务架构中,用以解决多线程环境下的数据隔离问题。 首先,...
本例以序列号生成的程序为例,展示ThreadLocal的使用
ThreadLocal是Java编程语言中的一个类,用于在多线程环境中提供线程局部变量。它是一种特殊类型的变量,每个线程都有自己的副本,互不影响,从而实现线程间数据隔离。ThreadLocal通常被用来解决线程共享数据时可能...
假设我们需要一个线程安全的计数器,可以使用ThreadLocal实现: ```java public class ThreadLocalCounter { private static ThreadLocal<Integer> counter = new ThreadLocal(); public static void increment...
关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. ...
这种方式在高并发的微服务架构中尤为常见,每个请求由独立的线程处理,每个线程都有自己的事务上下文。 在具体实践中,开发者需要根据业务场景合理选择事务管理方式和ThreadLocal的使用,以保证系统的性能和数据...
ThreadLocal 整理 ThreadLocal 是 Java 中的一个重要组件,它能够在每个线程中保持独立的副本。这个功能是通过 Thread 类中的 threadLocals 属性来实现的,这个属性实际上是一个 Entry 数组,其中的每个 Entry 都...
然后,通过当前线程获取对应的`ThreadLocalMap`实例,并基于`ThreadLocal`对象作为键查找对应的值。 2. **设置线程局部变量**:设置线程局部变量的过程类似。首先,获取当前线程和对应的`ThreadLocalMap`。如果当前...
它仍然基于数组,但是数组的元素类型从ThreadLocal的强引用改为了WeakReference。这样,当ThreadLocal对象不再被其他地方引用时,即使其在Map中,也会被垃圾收集器回收,降低了内存泄漏的风险。同时,弱引用的使用也...
SSH 配置多个数据库 SSH 配置多个数据库是在项目中遇到连接多个数据库的情况,并且根据用户的操作不同,连接不同的数据库,这时,就要动态切换数据库。本文将介绍如何使用 Spring 和 Hibernate 配置多个数据库,...
`在mysql中,分页的sql是使用limit来做的。...PageHelper会在mybatis执行sql前进行拦截,从ThreadLocal中取出分页参数,修改当前执行的sql语句,添加分页sql,最后执行了添加了分页的sql语句,实现分页查询
在通向架构师的道路中,ThreadLocal是一个重要的工具,它在Java多线程编程中起到关键作用,尤其是在优化层次划分时。ThreadLocal自JDK 1.2起就存在,它不是一个线程,而是一种管理线程局部变量的机制。每个线程都有...
ThreadLocal内部使用了一个ThreadLocalMap,它是一个基于ThreadLocal实例作为键,值为用户存储对象的弱引用表。每个线程都有一个这样的ThreadLocalMap,保证了线程间数据的隔离。 6. **工具支持** 在实际开发中,...
理解ThreadLocal 理解ThreadLocal 理解ThreadLocal 理解ThreadLocal
4. **性能优化**:在某些计算密集型应用中,`ThreadLocal`可以用于缓存线程局部的数据结构,减少不必要的数据同步和复制,提高程序的执行效率。 #### 六、总结 `ThreadLocal`是一种强大的工具,它简化了多线程编程...