最近同事在讨论一个关于分页的话题,我在此简单整理一下对于分页的认识。
首先,分页是什么层面上的事儿?是数据访问层面、业务层面还是展示层面?
对于数据访问层来说,具体说,对于查询接口,需要一个“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){}
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博客在数日后将不再更新。
分享到:
相关推荐
高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页高级分页...
分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码分页代码...
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 分页...
超强php分页打包 通用分页 万能分页 ajax分页 google分页
jsf 分页 jsf 分页 jsf 分页 jsf 分页
jQuery分页插件设置分页内容显示数量的分页代码 jQuery分页插件设置分页内容显示数量的分页代码
ajax 分页ajax 分页ajax 分页
ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询ext分页查询...
自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签...
js分页示例,前台分页,客户端分页,分页机制,js分页
各种分页样式 分页风格 分页css 分页cs.htm
hbase查询分页分页
分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具分页工具
html静态代码所写的分页 纯分页代码 源码易懂
分页控件分页控件分页控件分页控件分页控件分页控件分页控件分页控件分页控件分页控件分页控件分页控件分页控件分页控件分页控件
sql 高效分页存储过程 sql 高效分页存储过程 sql 高效分页存储过程 sql 高效分页存储过程 sql 高效分页存储过程
AspNetPager除提供默认的类似于DataGrid和GridView的PostBack分页方式外,还支持通过Url进行分页,象大多数asp程序中分页一样, Url分页方式允许用户通过在浏览器地址栏中输入相应的地址即可直接进入指定页面,也...
网页的分页网页的分页网页的分页网页的分页网页的分页网页的分页网页的分页网页的分页网页的分页