`
zpl3001
  • 浏览: 93397 次
  • 性别: Icon_minigender_1
  • 来自: baga
社区版块
存档分类
最新评论

分页技术的原理及其实现

阅读更多
分页问题是一个非常普遍的问题,开发者几乎都会遇到,这里不讨论具体如何分页,说明一下Web方式下分页的原理。首先是查询获得一个结果集(表现为查询数据库获得的结果),如果结果比较多我们一般都不会一下显示所有的数据,那么就会用分页的方式来显示某些数据(比如20条)。因为Http的无状态性,每一次提交都是当作一个新的请求来处理,即使是换页,上一次的结果对下一次是没有影响的。

这里总结三种实现分页的方式,不知道还有没有别的!
1.每次取查询结果的所有数据,然后根据页码显示指定的纪录。
2.根据页面只取一页数据,然后显示这一页,这里要构造sql语句。
3.取一定页数的数据,就是前面两种的折中。

这里还要注意的是这些数据是放在request还是session中,这里一一讨论

1.一般不会放在session中,因为会占用大量内存,所以要放在request里面。
优点:实现比较简单,查询速度比较快。
缺点:占用内存多一些,网络传输数据多。
对于数据量比较少的查询这种方法比较合适。这里有人把数据放在session中,这样换页的时候就不用重新查询,但是这样是极其不好的,强烈建议不要这样使用。

2.肯定不会放在session中,因为放在session中没有意义。
优点:占用内存少。
缺点:比较麻烦,必须先获得查询结果的总数,因为要知道有多少纪录才知道有多少页。另外要构造分页查询语句,对于不同的数据库是不一样的。

3.这种情况是肯定放在session中了,要不然我干吗取好几页呀,这样的实现是为了减少数据库查询的次数,比如我保存第1到10的纪录,那么换页的时候如果在1到10之间就可以直接从session获取。如果换到11页,我可以重新设置缓存11到
20页的数据(或者5到15页的数据),这样的话换10次才需要一次数据库查询操作。
优点:占用内存相对不多,提高平均查询速度。
缺点:实现起来更加复杂,可能存在脏数据,需要自己定义一个缓存集合。如果查询的数据量比较大,可以考虑采用这样方式。

很多人在分页问题上处理的不够好,往往导致整个程序混乱,代码可读性差,不过有经验的一般都会做一个JavaBean来实现分页功能,但是就是这个JavaBean如何设计又带来一大堆问题,有的甚至使程序逻辑更加混乱。这里推荐一篇文章:
http://blog.csdn.net/treeroot/articles/122802.aspx

设计这个分页功能要达到的目标:
1.和实现无关,这样才可以复用。
2.符合开闭原则,也就是说可以新增功能,但不需要修改。
3.简洁明了,容易看懂。


下面的设计每次只获取一页数据,每次都要重新设置查询总数,具体如何获得自己实现,这是一个比较通用的分页实现。

这里设计一个接口:
package treeroot.util;
import java.util.List;
/**
* 该接口用来实现分页功能,注意这里没有提供修改的功能。
* @author treerot
* @version 1.0
* @since 2004-9-30
*/
public interface Pageable
{
  /**
  * 获得数据结果
  * @return
  */
  public List getResult();
  /**
  * 获得查询总数
  * @return
  */
  public int getCount();
  /**
  * 获得每页纪录数
  * @return
  */
  public int getPageSize();
  /**
  * 获得当前页编号
  * @return
  */
  public int getCurrentPage();
  /**
  * 获得总页数
  * @return
  */
  public int getPages();
  /**
  * 每页默认显示纪录数
  */
  public final static int DEFAULT_PAGESIZE=20;

}
这个接口非常简单,就是包括一个结果列表和一些分页的必要信息,这里注意几点:
1.这个接口的实现表示的是某一次查询的某一页数据,和上次查询无关
2.这个接口的实现应该是只读的,也就是说不可以修改的。
3.getPages()方法是冗余的,但是这里仍然提供这个方法。

下面给出一个抽象实现:
package treeroot.util;
import java.util.List;
/**
* @author treerot
* @version 1.0
* @since 2004-9-30
*/
public abstract class AbstractPage implements Pageable
{
  private int currentPage;
  private int pageSize;
  private int pages; 
 
  protected int count;
  protected List result;

  /**
  * 指定当前页 
  * @param currentPage
  * @throws PageException
  */
  public AbstractPage(int currentPage){
    this(currentPage,Pageable.DEFAULT_PAGESIZE);
  }

  /**
  * 指定当前页和页大小
  * @param currentPage
  * @param pageSize
  * @throws PageException
  */
  public AbstractPage(int currentPage,int pageSize) {
    this.currentPage=currentPage;
    this.pageSize=pageSize;
  }

  protected void checkPage(int currentPage) throws PageException{
    if((currentPage<1)||(currentPage>this.getPages()))
       throw new PageException("页超出范围:总页数为"+this.getPages()+",当前页为"+currentPage);
  }
  /**
  * 这个方法被子类重写用来初始化,也就是计算count值和result结果,在子类 的构造函数中调用。
  */
  abstract protected void init() throws PageException;
  
  public List getResult()
  {
    return result;
  }
 
  public int getCount()
  {
    return count;
  }

  public int getPageSize()
  {
    return pageSize;
  }

  public int getCurrentPage()
  {
    return currentPage; 
  }
 
  public int getPages()
  {
    if(pages==0) this.pages=(count+pageSize-1)/pageSize;
    return pages;
  }
}
这个抽象类实现了接口中的所有方法,但是定义了一个抽象方法init(),在子类中必须实现这个方法。上面的一个接口和一个抽象类看起来比较简单,你可能会觉得好像什么都没有做,实现上确实没有做什么,但是却可以给开发带来很大的帮助。我们可以根据自己的需要要继承这个抽象类,而数据可以通过各种方式获得,比如直接通过一个List获得,或者通过JDBC,Hibernate等等,不过我们都需要把结果封装到一个List里面,通过Hibernate就显得特别方便了。

PageException是自定义的一个异常
package treeroot.util   
/**
* @author treeroot
* @version 1.0
* @since 2004-9-30
*/
public class PageException extends Exception
{
  public PageException(){
    super();
  }
  public PageException(String message){
    super(message);
  }
}

这里构建一个最简单的分页实现,也就是说通过查询结果的列表来构建页对象,这种情况是存在的:比如本次要显示的结果不是直接从数据库查询得到的,而是通过过滤某次数据库查询得到的,总之就是一个包含所有数据的结果集合。

不知道有没有说清楚,这里直接给出一个参考实现:
package treeroot.util;
import java.util.List;

/**
* @author treerot
* @version 1.0
* @since 2004-9-30
*/
public class ListPage extends AbstractPage implements Pageable
{
  private List list;

  /**
  * 通过一个结果列表来初始化
  * @param list
  * @throws PageException
  */
  public ListPage(List list) throws PageException
  {
    this(list, 1, Pageable.DEFAULT_PAGESIZE);
  }
  /**
  * 通过一个结果列表来初始化,指定当前页
  * @param list
  * @param currentPage
  * @throws PageException
  */
  public ListPage(List list, int currentPage) throws PageException
  {
    this(list, currentPage, Pageable.DEFAULT_PAGESIZE);
  }
  /**
  * 通过一个结果列表来初始化,指定当前页和页大小
  * @param list
  * @param currentPage
  * @param pageSize
  * @throws PageException
  */
  public ListPage(List list, int currentPage, int pageSize) throws PageException
  {
    super(currentPage, pageSize);
    //这里为了达到fail-fast,直接抛出异常,不是必须的。
    if(list==null) throw new NullPointerException();
   
    this.list = list;
    init();
  }

  protected void init() throws PageException
  {
    this.count = list.size();
    checkPage(this.getCurrentPage());
    int fromIndex = (this.getCurrentPage() - 1) * this.getPageSize();
    int toIndex = Math.min(fromIndex + this.getPageSize(), this.count);
    this.result = list.subList(fromIndex, toIndex);
  }

}

是不是觉得很简单,看一下怎么应用吧,假如你有一个方法就是获得查询结果,那么你就可以这样用了。

在Servlet或者Action中:
int currentPage=1;
String str=request.getParameter("currentPage");
if(str!=null){
  try{
     currentPage=Integer.parseInt(str);
  }
   catch(NumberFormatException e){} 
}

List list= .... //这里获得所有结果,可能是直接获得数据库查询结果,也可能经过处理。
Pageable pg =null;
try{
  pg= new ListPage(list,currentPage);
        //或者通过Dao pg=(new Dao()).getResutl(currentPage); 返回类型为Pageable
}
catch(PageException e)
{
  pg=null;
}
request.setAttribute("page",pg);
//转发给一个JSP页面显示。

是不是觉得很简洁?当然这里没有给出具体的的数据获取方法,但是在JSP显示页面就会觉得很舒服了,这里简单写一个JSP。
<%
Pageable pg=(Pageable)request.getAttribute("page");
%>
<table>
<tr>
<th>学号</th>
<th>姓名</th>
</tr> 
<%
if(pg!=null){
  List list=pg.getResult();
  for(int i=0;i<list.size();i++){
    Student stu=(Student)list.get(i);
%>
    <tr>
    <td><%=stu.getNumber()%></td>
    <td><%=stu.getName()%></td>
    </tr>
<%
  }
%>

  <tr>
  <td colspan="2">
  总纪录:<%=pg.getCount()%> &nbsp;&nbsp;
  每页显示:<%=pg.getPageSize()%> &nbsp;&nbsp
  页次:<%=pg.getCurrentPage()%>/<%=pg.getPages()%> &nbsp;&nbsp;
  <a href="#" onClick="gotoPage(<%=pg.getCurrentPage()-1%>)">上一页</a>
   <a href="#" onClick="gotoPage(<%=pg.getCurrentPage()+1%>)">上一页</a>
  </td>  
  </tr>

<%
}
else{
%>
  <tr>
  <td colspan="2">指定的页不存在</td>
  </tr>
<%
}
%>
这里只是简单得描述了一下,gotoPage是一个javascript函数,就是提交一个表单,指定当前页码。这里有些问题都没有处理:比如页码越界(可以在客户端也可以在服务器端验证,可以报错也可以自动纠正)。

我开始就是为了在Hibernate中使用分页才设计这个分页实现的,因为使用Hibernate时,查询后的结果被自动封装到一个List中了,所以使用起来特别方便,这里我做了一个比较庸俗的实现,就是查询参数只适合字符串类型,不过大部分查询还真的只是对字符串操作。

package treeroot.util;

import net.sf.hibernate.HibernateException;
import treeroot.common.dao.AbstractBaseDao;

/**
* @author treerot
* @version 1.0
* @since 2004-9-30
*/
public class HibernatePage extends AbstractPage implements Pageable
{
  private String querySql;
  private String countSql;
  private String[] parameters;

  public HibernatePage(int currentPage,String querySql,String countSql,String[] parameters) throws PageException{
    this(currentPage,Pageable.DEFAULT_PAGESIZE,querySql,countSql,parameters);
  }
  public HibernatePage(int currentPage,int pageSize,String querySql,String countSql,String[] parameters) throws PageException
  {
    super(currentPage, pageSize);
    this.querySql = querySql;
    this.countSql = countSql;
    this.parameters = parameters;
    init();
  }
  protected void init() throws PageException
  {
    try{
        this.count = AbstractBaseDao.queryCount(countSql);
       int fromIndex = (this.getCurrentPage() - 1) * this.getPageSize();
       int toIndex = Math.min(this.count, fromIndex + this.getPageSize());
       this.result = AbstractBaseDao.find(this.querySql,this.parameters,fromIndex,toIndex);
    }
    catch (HibernateException e)
    {
       throw new PageException(e.getMessage());
    }
  }
}
这个类的设计并不是很合理的,因为查询只能接受字符串参数,但是如果只需要字符串参数就足够了。另外查询语句必须是JDBC风格的参数(?), 而不是Hibernate风格的(:=),你看过Dao里面的代码就知道为什么了。先看一下如何使用吧(一个Dao中的方法):

public Pageable findByName(String name,int currentPage) throws PageException{
  String countSql="select count(*) from MyClass as c where c.name like ?";
  String querySql="from MyClass as c where c.name like ?";
  String[] parameter=new String[]{name};
  return new HibernatePage(currentPage,countSql,querySql,parameter);
}

这个方法应该是比较简洁的,这里给出queryCount和find的实现,我对Hibernate的了解比较肤浅,所以下面的方法如果有什么不当的地方还望指出,谢谢!

public static int queryCount(String hql, String[] args) throws HibernateException
{

  if (hql == null) throw new NullPointerException();
  Object obj = null;
  Transaction trans = null;
  Session s = null;
  try
  {
    s = HibernateSessionFactory.currentSession();
    trans = s.beginTransaction();
    Query q = s.createQuery(hql);
    if (args != null)
    {
      for (int i = 0; i < args.length; i++){
        q.setString(i, args[i]);
      }
    }
    obj = q.uniqueResult();
    trans.commit();
  }
  catch (HibernateException e)
  {
    if (trans != null)
    {
      try{
        trans.rollback();
      }
      catch (HibernateException ex){//no need to care this Exception }
    }
    throw e;
  }
  return ((Integer) obj).intValue();
}

public static List find(String hql, String[] args, int fromIndex, int toIndex) throws HibernateException
{
  if (hql == null) throw new NullPointerException();
  List l = null;
  Transaction trans = null;
  Session s = null;
  try{
    s = HibernateSessionFactory.currentSession();
    trans = s.beginTransaction();
    Query q = s.createQuery(hql);
    if (args != null){
      for (int i = 0; i < args.length; i++){
        q.setString(i, args[i]);
      }
    }
    if (fromIndex > -1){
       if (toIndex > fromIndex){
         q.setFirstResult(fromIndex);
         q.setMaxResults(toIndex - fromIndex);
       }
       else{
         throw new IndexOutOfBoundsException();
       }
    }
    l = q.list();
    trans.commit();
   }
   catch (HibernateException e){
     if (trans != null){
       try{
         trans.rollback();
       }
       catch (HibernateException ex){ //no need to care this Exception }
     }
     throw e;
   }
   return l;
}





分享到:
评论

相关推荐

    新浪分页技术,挺不错的分页技术思路

    下面将详细介绍新浪分页技术及其核心思想。 一、分页的基本原理 分页技术的核心是将大量数据分割成若干个较小的部分,每个部分对应一个页面,用户可以逐页浏览,而不是一次性加载所有数据。这样既节省了服务器资源...

    网页分页技术(一看就会的)

    ### 网页分页技术详解 ...通过本篇文章的学习,初学者应该能够掌握分页的基本原理及其实现方法。在实践中,可以根据具体需求调整分页策略,例如增加更多的导航选项或者动态调整每页显示的数量等。

    jsp分页功能实现小程序

    下面我们将深入探讨这个JSP分页功能的实现及其相关知识点。 首先,JSP是Java Web应用中的视图层技术,它允许开发者在HTML页面中嵌入Java代码,从而动态生成页面内容。在这个分页项目中,JSP将用于构建用户界面,...

    在DataGridView中实现分页效果

    下面将详细介绍该类的主要功能及其实现过程。 ### 类结构分析 #### 属性定义 - `private int _RowsPerPage;`:每页显示的行数。 - `private int _TotalPage;`:总页数。 - `private int _curPage = 0;`:当前页码,...

    java中的分页技术

    本篇将详细介绍Java中的分页技术及其相关知识点。 1. 分页原理 分页的基本思想是将数据库中的海量数据分为若干页进行显示,用户每次请求只获取一部分数据,而不是一次性获取所有数据。这既节省了服务器资源,又...

    Oracle,SQl,MySql实现分页查询

    本文将基于提供的文件信息,深入探讨三种不同的分页查询方法及其在Oracle、SQL Server(这里用SQL代替)、MySQL中的实现方式,并对它们的性能进行对比分析。 #### 分页方案一:利用Not In和SELECT TOP分页 **基本...

    java分页工具类,以及基于Struts,Spring,mybatis,jsp中分页的调用及实现

    ### Java分页工具类及其在Struts、Spring、MyBatis和JSP中的应用 #### 一、Java分页概述 在开发Web应用程序时,为了提高用户体验并减轻服务器...希望本文能够帮助开发者更好地理解和掌握Java分页技术的实现与应用。

    dwr框架实现无刷新分页

    **DWR(Direct Web Remoting)框架实现无刷新分页技术详解** DWR(Direct Web Remoting)是一种JavaScript和Java之间的远程调用技术,它允许Web应用在不刷新整个页面的情况下与服务器进行交互,从而实现动态更新和...

    操作系统实验分页管理

    通过实际操作,学生可以更直观地看到虚拟地址到物理地址转换的过程,理解请求分页管理的重要性和其实现机制。 总之,通过本次实验的学习,学生能够更加深入地理解操作系统内存管理的核心概念和技术细节。

    java分页技术

    Java分页技术是Java开发中常见的一种数据处理方式,尤其在大数据量的Web应用中,为了提高用户体验,避免一次性加载过多数据导致页面响应慢或内存压力增大,分页成为必不可少的功能。本文将深入探讨Java中分页技术的...

    ajax分页技术 还蛮不错的 可以看看

    本文将深入探讨AJAX分页技术,揭示其工作原理、优势以及具体应用场景,帮助读者全面理解这一关键技术。 #### AJAX分页技术概述 AJAX分页技术是AJAX技术在网页分页功能上的具体应用。传统网页分页在用户请求下一页...

    相关分页技术

    本文将深入探讨JSP中的分页技术及其应用。 一、理解分页基础概念 1.1 分页原理:分页是将大量数据分成多个部分,每次只显示一部分,用户可以通过点击页码或者导航按钮来浏览不同部分的数据。这样既能保持页面加载...

    Asp.Net(c#)文章内容分页

    虽然示例可能针对的是动态内容,但稍加修改,你就可以将这个分页技术应用于静态页生成的CMS系统。主要的调整可能包括预处理静态页面内容,将分页信息嵌入到HTML中,然后在用户访问时根据URL参数加载对应页码的内容。...

    简单易懂的分页技术

    本篇将基于提供的文件内容,深入解析简单的分页技术及其实现原理。 #### 分页技术简介 分页是指将数据分割成多个页面进行显示的技术。它能够有效避免一次性加载大量数据导致的页面加载缓慢问题,并且可以减少用户...

    分页(一级栏目分页)

    本文将深入探讨如何实现"分页(一级栏目分页)",以及在PHP CMS中如何应用这一技术。 一、分页原理 分页是将大量数据分成若干部分,每次只加载一部分到用户界面,以提高页面加载速度,减轻服务器压力,同时提供更好...

    js实现无刷新分页

    1. **jQuery 插件**:对于不熟悉原生 JavaScript 的开发者,可以使用 jQuery 库及其丰富的分页插件,如 `bootstrap-paginator` 或 `jquery.twbsPagination`。 2. **Vue.js、React.js、Angular.js 等框架**:在现代...

    操作系统分页的三种实现方法

    操作系统中的分页技术是内存管理的一种重要机制,它将连续的物理内存空间划分为固定大小的块,称为页面,同时将进程的逻辑地址空间也划分为相同大小的块,称为页框。虚拟分页则是在此基础上引入了页表和缺页机制,以...

    关于jsp方面分页技术

    分页技术是JSP中常见的需求,特别是在处理大量数据展示时,如论坛、电商网站的产品列表等。它能有效地提高用户体验,避免一次性加载过多的数据导致页面响应慢或者内存压力过大。 在提供的代码中,`FenYe` 类实现了...

    图片分页功能

    在这个“图片分页功能”的项目中,开发者使用Java语言实现了一个高效且完善的分页解决方案,这对于学习和理解分页原理及其实现方式具有很高的价值。 1. **Java分页基础** Java中的分页通常涉及到数据库操作,如SQL...

    asp 数字分页 论坛分页

    下面我们将详细讨论ASP中的数字分页实现原理及其应用。 1. 分页原理: ASP数字分页的基本思想是通过计算总页数和当前页数,然后根据用户请求的页码显示对应的数据块。首先,我们需要知道数据总数(通常通过SQL查询...

Global site tag (gtag.js) - Google Analytics