`

分页的那些事儿

阅读更多

最近同事在讨论一个关于分页的话题,我在此简单整理一下对于分页的认识。

首先,分页是什么层面上的事儿?是数据访问层面、业务层面还是展示层面?

对于数据访问层来说,具体说,对于查询接口,需要一个“from”参数和一个“to”参数,就可以做到获取查询结果集中特定的记录了,它不应该知道任何关于第几页和每页有几条数据这样的信息,这种信息应该是在上层的展示层面所关心的。

举例来说,有这样的接口调用(这只是其中一种接口形式,关于DAO接口的形式可以参见这篇文章的讨论):

1
2
3
4
map.put("age", 18);
map.put("from", 3);
map.put("to", 5);
List<User> userList = userDao.list(map);

其次,可以达成共识的是,分页功能应该由一个工具来实现,这个工具不应该知道任何业务逻辑,应该与其它部分解耦开。

分页工具可以是一个简单的计算工具,连实际的数据都不需要给它,只需要指定总数和每页大小:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class PaginationSupport {
    //构造器
    public PaginationSupport(int totalCount, int pageSize){}
     
    //翻页
    public void turnToNextPage(){}
    public void turnToPreviousPage(){}
    public void setPageNo(int pageNo){}
 
    //数据访问层需要的start和end
    public int getStart(){}
    public int getEnd(){}
 
    //是否还有上一页、下一页
    public boolean hasNextPage(){}
    public boolean hasPreviousPage(){}
 
    //当前在哪一页
    public int getPageNo(){}
 
    ... ...
}

翻页任意次之后,调用getStart/getEnd方法就能输出DAO需要的“start”和“end”参数的值。这是一种最纯粹的分页工具了,实现也非常简单,下面我们来把它弄复杂一点。

如果在内存里分页,借助模板参数,稍稍修改一下成下面这种形式,把数据给它,让它来帮你返回显示页需要的结果集合:

1
2
3
4
5
6
7
class PaginationSupport<T> {
    //构造器
    public PaginationSupport(List<T> items, int pageSize){}
     
    //其它主要方法不变
    ... ...
}

可是,这种形式需要置入一个List<T>,如果我想在数据库分页,而非内存分页,这种形式就做不到了。那能不能提供一种兼容二者的通用方式呢?

可以。引入这样一个接口:

1
2
3
4
interface DataCollection<T>{
    public List<T> getData(int start, int end);
    public int getCount();
}

而PaginationSupport这个类统一成如下形式:

1
2
3
4
5
6
7
8
9
class PaginationSupport<T> {
    //构造器
    public PaginationSupport(DataCollection<T> dc, int pageSize, int startPageNo){}
 
    //获取当前页数据
    public List<T> getData(){}
 
    ... ...
}

这样一来,如果是内存分页,就在DataCollection接口的实现中,操纵这个数据list:

1
2
3
4
5
6
7
8
9
PaginationSupport ps = new PaginationSupport<User>(new DataCollection<User>() {
    List<User> list = xxx;
    public int getCount() {
        return list.size();
    }
    public List<Object> getData(int start, int end) {
        return list.subList(start, end);
    }
}, 10);

而如果是需要DAO层start和end参数传入的分页,则在DataCollection接口的实现中,调用DAO:

1
2
3
4
5
6
7
8
9
10
11
PaginationSupport ps = new PaginationSupport<User>(new DataCollection<User>() {
    public int getCount() {
        return userDao.count();
    }
    public List<User> getData(int start, int end) {
        ... ...
        map.put("start", start);
        map.put("end", end);
        return userDao.list(map);
    }
}, 10);

需要补充的是,如果只需要下面这种调用DAO接口来实现的查询分页,分页的工作还可以进一步改进,因为将“start”和“end”这两个参数注入map的过程,完全可以让框架来完成——从分页工具开始,直到数据库访问的SQL代码为止,开发人员都可以不关心这两个参数,让框架来拼接这个查询子句。这样的话,DataCollection接口就会变成这个样子:

1
2
3
4
interface DataCollection<T>{
    public List<T> getData(Map queryMap);
    public int getCount();
}

其中getData方法的queryMap参数,分页工具已经给预置好了一些协助查询的参数,开发人员不需要手动构造和添加这样的参数了。

好处仅仅是这么多吗?不是。分页工具只是做分页这一件事没错,但是框架可以利用它,在外面做很多额外的事情。比如,在接口改成如上的形式时,我们还可以做到对分页查询结果的缓存完全透明化,开发人员连缓存条目的key都不需要提供。如果我们规约好查询方法都叫“list”,框架获知当前查询的模型T,完整查询的queryMap对象,以这两个作为生成key的因子(如果该模型的查询方法不止一个,那就还需要查询方法名也作为因子提供),比如生成了这样一个key:

1
User_age=18_start=3_end=7

之后就可以根据一定的配置帮助缓存起查询的结果来,比如:

1
com.company.xxx.User.list=30000

表示User的查询缓存记录的过期时间是30000毫秒。

最后来说说查询子句不一致的问题。分页查询的SQL子句大不相同,比如Oracle会用到rownum,而MySQL又需要limit,所以一种方法是在DAO层屏蔽这样的差异。

不过,还有一个思路是利用JDBC来屏蔽差异,它提供了结果集对象,很适合我们做这件事(java.sql.ResultSet接口的next和previous方法),比如我们可以自己定义一个名为PageableResultSet这样的接口来继承自ResultSet接口。不过需要注意的是,这个接口可没有提供跳到任意记录的方法,因此在实现的时候只能借由游标走,一行一行记录地next或者previous。我见过的几个项目都没有用这个方式来实现的,关于它的优劣,欢迎你来给我分析分析。

文章系本人原创,转载请注明作者和出处(http://www.raychase.net

注:本博客已经迁移到个人站点 http://www.raychase.net/ ,欢迎大家访问收藏,本ITEye博客在数日后将不再更新。

 

2
4
分享到:
评论
6 楼 alixjiang 2015-01-27  
答案在风中 写道
好的分页应当把显示逻辑和处理逻辑分开,不一定需要10条我只去请求10条,多取一部分并做缓存,这样在后续的访问中就不需要再请求了。而且避免过多的零散的缓存,

还是要看业务需求的,如果对数据的准确性和实时性要求高的话,这种方式并不适合。
5 楼 xunke515 2013-08-29  
答案在风中 写道
好的分页应当把显示逻辑和处理逻辑分开,不一定需要10条我只去请求10条,多取一部分并做缓存,这样在后续的访问中就不需要再请求了。而且避免过多的零散的缓存,

学习了~
4 楼 weareyou-006 2012-11-14  
不错!
3 楼 答案在风中 2012-10-26  
好的分页应当把显示逻辑和处理逻辑分开,不一定需要10条我只去请求10条,多取一部分并做缓存,这样在后续的访问中就不需要再请求了。而且避免过多的零散的缓存,
2 楼 gaozhonghui 2012-10-24  
map.put("age", 18);
map.put("from", 3);
map.put("to", 5);
List<User> userList = userDao.list(map);
1 楼 freezingsky 2012-10-13  
分页是个经常谈起的话题了,其实分页可以分为三种:客户端分页,服务器端分页,还有数据库分页,相对于不同的场景需求,选择对应的分页方式。

相关推荐

    高级分页高级分页高级分页高级分页

    高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页...

    分页代码 分页代码 分页代码

    分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码...

    超强php分页打包 通用分页 万能分页 ajax分页 google分页

    本压缩包“超强php分页打包 通用分页 万能分页 ajax分页 google分页”提供了一系列的分页解决方案,包括了基本的PHP分页、通用的分页实现、以及结合AJAX技术的动态分页,旨在满足各种项目需求。下面将详细介绍这些...

    java实现分页 jsp分页 分页

    java实现分页 jsp分页 分页java实现分页 jsp分页 分页java实现分页 jsp分页 分页java实现分页 jsp分页 分页java实现分页 jsp分页 分页java实现分页 jsp分页 分页

    jsp 分页jsp 分页

    jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页jsp 分页...

    数据库分页数据库分页数据库分页数据库分页

    ### 数据库分页技术详解 #### 一、引言 在大数据时代,高效的数据管理和查询技术变得尤为重要。其中,数据库分页技术作为一种提高用户体验、优化数据检索效率的重要手段,在各类应用系统尤其是Web应用中得到了广泛...

    分页插件.zip

    在网页开发中,分页是不可或缺的一个功能,特别是在数据量庞大的时候,为了提高用户体验,将大量数据分成多个页面展示,而不是一次性加载所有内容。本文将详细介绍一个基于jQuery的分页插件,它具有代码简洁、兼容性...

    Java Ajax分页,jsp ajax分页

    在网页开发中,分页是一种常见的用户界面设计,用于处理大量数据时的高效展示。Java AJAX(Asynchronous JavaScript and XML)分页与JSP(JavaServer Pages)相结合,可以提供无需刷新整个页面即可动态加载更多内容...

    js分页示例,前台分页,客户端分页,分页机制,js分页

    本篇将深入探讨JavaScript实现分页的相关知识点,包括前台分页、客户端分页、分页机制以及相关的JavaScript代码示例。 首先,我们要理解什么是分页。在Web应用中,分页是将大量数据分成多个小部分(每部分通常称为...

    分页:高度自定义分页

    2.1.目前网络上很多分页主键,不能进行按钮选择, 2.2.甚至对样式修改的难度也较大, 2.3.使用在开发过程中由于使用到了分页功能,在这里写了一个分页功能。 3.按钮选择 之前的很多按钮是通过参数进行选择,这样不...

    jsf分页 jsf分页 jsf分页

    在JavaServer Faces (JSF)框架中,分页是一种常用的技术,用于处理大量数据时提供更好的用户体验。当数据集过大,一次性加载所有记录到页面上会导致性能下降且用户界面响应变慢。通过分页,我们可以将数据分成多个...

    JS分页效果JS分页效果JS分页效果

    JavaScript(简称JS)分页效果是网页开发中常见的功能,尤其在处理大量数据时,用于提高用户体验和加载速度。此项目中的"JS分页效果"指的是利用JavaScript和相关的库(如jQuery)实现对数据进行分页显示的技术。下面...

    用recyclerview实现分页滑动,横向纵向分页

    本教程将深入讲解如何利用RecyclerView实现横向和纵向的分页滑动,充分利用Android原生功能。 首先,理解RecyclerView的基本结构。RecyclerView包含一个Adapter,它负责将数据集绑定到视图上,以及一个...

    java 通用分页 java 通用分页

    Java 通用分页详解 Java 通用分页是指在Java编程中对大量数据进行分页处理,以提高系统效率和性能。下面对Java通用分页的知识点进行详细说明: 1. 分页的必要性 在实际项目中,数据量可能非常大,直接查询所有...

    hbase查询分页分页

    时间戳分页适用于那些行键本身无特定顺序,但有时间属性的场景。每次查询指定一个时间范围,例如最近10条记录。而自定义的分页令牌则是在查询结果中保存最后一个元素的某些信息(如行键的一部分或哈希值),用作下次...

    java分页标签自定义分页标签自定义分页标签

    自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签...

    分页+递归显示分页+递归显示

    在IT领域,分页和递归是两种常见的数据处理技术,尤其在大数据量的Web应用中,它们扮演着至关重要的角色。"分页+递归显示分页+递归显示"这一主题涉及到如何有效地管理和展示大量层级数据。下面将详细阐述这两个概念...

    QTableWidget,QTableView分页的分页栏

    为了解决这个问题,我们可以实现分页功能,让数据分批次加载,从而提高界面响应速度。本文将深入探讨如何在`QTableWidget`和`QTableView`中实现分页栏。 首先,我们需要理解`QTableWidget`和`QTableView`的区别。`...

    各种分页样式 分页风格 分页css

    各种分页样式 分页风格 分页css 分页cs.htm

    分页工具分页工具分页工具

    分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具

Global site tag (gtag.js) - Google Analytics