`
yuehan
  • 浏览: 8028 次
  • 性别: Icon_minigender_1
  • 来自: 成都
文章分类
社区版块
存档分类
最新评论

透明的分页实现,基于ThreadLocal。采用SSH架构为例执行说明

阅读更多

在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方法中写分页相关逻辑,没成功。有实现了的同志不妨指点指点。

 

1
2
分享到:
评论
2 楼 yuehan 2011-05-28  
补充一点:此分页标签不同于一般的分页标签。此标签不负责数据循环输出,所以也没有传递后端list对象。这样极少的侵入。跟普通循环输出基本没有区别,只是在要显示上一页,下一页的地方写上<app:page href='' />
例如:
  <table width="100%;" cellpadding="0" cellspacing="0" class="mctb">
    <tr class="mctb_head">
      <td>编号</td>
      <td>用户</td>
      <td>密码</td>
      <td>邮件</td>
    </tr>
    <s:iterator value="userList">
      <tr class="mctb_row">
        <td><s:property value="sn"/></a></td>
        <td><s:property value="username" /></td>
        <td><s:property value="password" /></td>
        <td><s:property value="email" /></td>      
      </tr>
    </s:iterator>
    <tr class="mctb_bottom">
      <td colspan="4">
        <app:page uri="getUser.do"></app:page><%-- 此标签生成类似 <a href='xx'>上一页</a> ... <a href='xx'>下一页</a> --%>
      </td>
    </tr>
  </table>
1 楼 Technoboy 2011-05-28  
用ThreadLocal,想法不错啊!

相关推荐

    ThreadLocal原理及在多层架构中的应用

    ThreadLocal的工作原理主要基于以下几点: - **内部类ThreadLocalMap**:ThreadLocal在每个线程内部维护了一个名为ThreadLocalMap的哈希映射表,这个表的键是ThreadLocal对象,值是线程局部变量的实际值。这样,每...

    ThreadLocal原理及在多层架构中的应用.pdf

    ThreadLocal是一种在多线程环境下为每一个线程提供各自变量副本的机制,以确保线程安全。在Java中,ThreadLocal被广泛应用于Web中间件、服务端编程和微服务架构中,用以解决多线程环境下的数据隔离问题。 首先,...

    线程ThreadLocal机制实现例子

    本例以序列号生成的程序为例,展示ThreadLocal的使用

    ThreadLocal

    ThreadLocal是Java编程语言中的一个类,用于在多线程环境中提供线程局部变量。它是一种特殊类型的变量,每个线程都有自己的副本,互不影响,从而实现线程间数据隔离。ThreadLocal通常被用来解决线程共享数据时可能...

    ThreadLocal应用示例及理解

    假设我们需要一个线程安全的计数器,可以使用ThreadLocal实现: ```java public class ThreadLocalCounter { private static ThreadLocal&lt;Integer&gt; counter = new ThreadLocal(); public static void increment...

    ThreadLocal详解及说明

    关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. ...

    java事务 - threadlocal

    这种方式在高并发的微服务架构中尤为常见,每个请求由独立的线程处理,每个线程都有自己的事务上下文。 在具体实践中,开发者需要根据业务场景合理选择事务管理方式和ThreadLocal的使用,以保证系统的性能和数据...

    ThreadLocal整理.docx

    ThreadLocal 整理 ThreadLocal 是 Java 中的一个重要组件,它能够在每个线程中保持独立的副本。这个功能是通过 Thread 类中的 threadLocals 属性来实现的,这个属性实际上是一个 Entry 数组,其中的每个 Entry 都...

    java中ThreadLocal详解

    然后,通过当前线程获取对应的`ThreadLocalMap`实例,并基于`ThreadLocal`对象作为键查找对应的值。 2. **设置线程局部变量**:设置线程局部变量的过程类似。首先,获取当前线程和对应的`ThreadLocalMap`。如果当前...

    ThreadLocal_ThreadLocal源码分析_

    它仍然基于数组,但是数组的元素类型从ThreadLocal的强引用改为了WeakReference。这样,当ThreadLocal对象不再被其他地方引用时,即使其在Map中,也会被垃圾收集器回收,降低了内存泄漏的风险。同时,弱引用的使用也...

    ssh配置多个数据库

    SSH 配置多个数据库 SSH 配置多个数据库是在项目中遇到连接多个数据库的情况,并且根据用户的操作不同,连接不同的数据库,这时,就要动态切换数据库。本文将介绍如何使用 Spring 和 Hibernate 配置多个数据库,...

    Mybatis PageHelper分页插件是一个应用于Mybatis中的分页插件系统.rar

    `在mysql中,分页的sql是使用limit来做的。...PageHelper会在mybatis执行sql前进行拦截,从ThreadLocal中取出分页参数,修改当前执行的sql语句,添加分页sql,最后执行了添加了分页的sql语句,实现分页查询

    通向架构师的道路(第七天)之漫谈使用ThreadLocal改进你的层次的划分

    在通向架构师的道路中,ThreadLocal是一个重要的工具,它在Java多线程编程中起到关键作用,尤其是在优化层次划分时。ThreadLocal自JDK 1.2起就存在,它不是一个线程,而是一种管理线程局部变量的机制。每个线程都有...

    使用ThreadLocal管理“session”数据

    ThreadLocal内部使用了一个ThreadLocalMap,它是一个基于ThreadLocal实例作为键,值为用户存储对象的弱引用表。每个线程都有一个这样的ThreadLocalMap,保证了线程间数据的隔离。 6. **工具支持** 在实际开发中,...

    理解ThreadLocal

    理解ThreadLocal 理解ThreadLocal 理解ThreadLocal 理解ThreadLocal

    正确理解ThreadLocal.pdf

    4. **性能优化**:在某些计算密集型应用中,`ThreadLocal`可以用于缓存线程局部的数据结构,减少不必要的数据同步和复制,提高程序的执行效率。 #### 六、总结 `ThreadLocal`是一种强大的工具,它简化了多线程编程...

Global site tag (gtag.js) - Google Analytics