`
zhangshixi
  • 浏览: 675227 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

构建自己的通用分页组件(上)

阅读更多

1. 需求:
   在实际项目开发中,分页是我们常见的操作,在一般数据展示的列表页,都会使用到数据分页。分页时,在每个页面上只需取得该页面展示的数据及列出其他的页码即可,这样可以以合适的粒度来获取页面展示的数据,避免不必要的数据的传输。
   在软件的分层构架中,实现一个供前后台交互用的分页组件,已成为每个项目必不可少的潜在需求。本文将在实际项目中分页需求的基础上,讨论并实现一个通用的分页组件,然后,可将其收录到自己的工具箱中,在不同的项目中导入复用即可。


2. 构建Page类:
   我们知道,在实际分页时,前台调用者在调用后台业务方法分页取得数据时,需告诉业务方法当前取第几页的数据以及每页取多少条数据;并希望返回的结果集中不仅包含当前页面所需要的数据列表,还要包含总数据量和总页面数。这样在前台展示的时候,就可以在展示当前页面数据时,又能将所有的分页信息展示给用户,以供用户作为访问其他页面数据的导航之用。因此,我们可以构造一个Page类,来表示分页信息,如下:

public class Page {
    // 当前页面的页数
    private int pageIndex;
    // 页面大小
    private int pageSize;
    // 数据总量
    private int totalData = -1;
    // 剩余数据量
    private int surplusData;
    // 页面总量
    private int totalPage;
    // 是否仅取第一页
    private boolean firstPage;
    // 是否可以工作。默认为false,只有设置了数据总量才为true。
    private boolean ready = false;
}

    该Page类包含了当前页数、每页的数据量、总数据量、剩余数据量、总页面量、是否仅取第一页以及该分页对象是否已经准备好,可以正常工作的标志。这样,调用者在调用后台业务方法进行分页查询数据时,只要构造一个指定当前页数pageIndex和页面大小pageSize的分页对象page,作为参数传递给后台业务方法即可。因此,根据实际需要,我们提供以下构造Page对象的构造方法:

// 默认当前页面页数为第一页
private static final int DEFAULT_PAGE_INDEX = 1;
// 默认页面大小为10
private static final int DEFAULT_PAGE_SIZE = 10;

/**
 * 以默认当前页面和页面大小构造一个分页对象。
 * 其中,默认当前页数为1,默认页面大小为10。
 */
public Page() {
    this.pageIndex = DEFAULT_PAGE_INDEX;
    this.pageSize = DEFAULT_PAGE_SIZE;
}

/**
 * 以指定的当前页面页数和页面大小构造一个分页对象。
 * @param pageIndex 当前页数,若参数值不大于0,则使用默认值1。
 * @param pageSize 页面大小,若参数值不大于0,则使用默认值10。
 */
public Page(int pageIndex, int pageSize) {
    this.pageIndex = pageIndex > 0 ? pageIndex : DEFAULT_PAGE_INDEX;
    this.pageSize = pageSize > 0 ? pageSize : DEFAULT_PAGE_SIZE;
}

/**
 * 以指定的页面大小构造一个表示第一页的分页对象。
 * @param pageSize 页面大小,若参数值不大于0,则使用默认值10。
 * @return 构造好的第一页分页对象。
 */
public static Page newFirstPage(int pageSize) {
    Page page = new Page(1, pageSize);
    page.setFirstPage(true);
    page.setTotalData(pageSize);

    return page;
}

   无参构造方法Page()将构造一个默认当前为第1页,每页取10条数据的分页对象;Page(int pageIndex, int pageSize)可以构造一个指定当前页数和页面大小的分页对象;newFirstPage(int pageSize)为一静态方法,此方法可以以指定的页面大小构造一个表示只取第一页数据的分页对象,这种方式将不会计算数据总量和页面总量,出返回第一页的数据外,不做任何处理,以满足我们在一些首页只取前几条数据的需求。
   至此,Page类的基本属性已定,下面我们看一下如何添加一些方法,利用这个Page类来实现前后台的交互。

 

3. 添加方法:
   从上面的需求分析中可知,根据调用者传递的page对象参数,后台业务方法应根据此参数查询出当前页面的数据列表、总数据量和总页面数返回给调用者。那么,我们抛开数据持久层具体实现技术不管,除了在底层采用分页技术查询出当前页面的数据外,如果调用者不是只取第一页,那么还应该查询出总的数据量,并设置到page对象中,由于知道了总数据量和每页显示的数据量,我们就能够计算出总页面数,剩余数据量等信息。因此,我们希望,在设置总数据量之后,page对象能够自动将这些信息计算好,所以我们在Page类中提供了设置总数据量的方法如下:

/**
 * 设置数据总量。在使用时,需提前调用此方法进行设置。
 * 当数据总量设置好之后,会计算页面总量、修正当前页面页数、计算剩余数据量,
 * 并设置该分页对象已经准备好,可以正常工作。
 * @param totalData 数据总量。
 */
public void setTotalData(int totalData) {
    this.totalData = totalData;

    this.totalPage = this.totalData / pageSize +
            (this.totalData % pageSize == 0 ? 0 : 1);

    if (this.pageIndex > this.totalPage) {
        this.pageIndex = this.totalPage;
    }

    this.surplusData = this.totalData - (this.pageIndex - 1) * this.pageSize;

    this.ready = true;
}

   在设置完总数据量之后,分页对象page就能正常工作了。持久层在设置完数据总量后,必须取得当前要查询数据的起始行号和结束行号,以便对数据库进行分页查询。因此,我们在Page类中还必须提供获取当前分页的页面范围的方法,如下:

/**
 * 获取当前分页对象的页面范围,包含当前页面的起始行和结束行。
 * 如果未先调用setTotalData()方法设置数据总量,则会抛出运行时异常。
 * @return 当前分页对象的页面范围。
 * @throws java.lang.IllegalArgumentException 异常。
 */
public PageScope getPageScope() throws IllegalArgumentException {
    if (!ready) {
        throw new IllegalArgumentException("Page has not seted data amount.");
    }

    if (this.pageIndex > this.totalPage) {
        return null;
    }

    PageScope scope = new PageScope();
    int startLine = (this.pageIndex - 1) * this.pageSize;
    int endLine;
    if (this.surplusData < this.pageSize) {
        endLine = startLine + this.surplusData - 1;
    } else {
        endLine = startLine + this.pageSize - 1;
    }
    if (startLine < 0) {
        startLine = 0;
    }
    if (endLine < 0) {
        endLine = 0;
    }
    scope.setStartLine(startLine);
    scope.setEndLine(endLine);

    return scope;
}

   PageScope类作为标识当前页面的页面范围信息。它只有两个属性:起始行号和结束行号。如下:

public class PageScope {
    // 起始行号
    private int startLine;
    // 结束行号
    private int endLine;
}

   这样,后台业务方法通过对指定页面数据的分页查询和对时局总量的设定,把调用者所需要的页面数据和其他页面信息包装到page对象中返回,就实现了我们通用的分页需求。


4. 如何使用?
   在介绍完分页类Page和页面范围类PageScope后,我们来看一下,如何使用自定义的分页组件来简化我们的实际开发。下面我们以分页查询用户列表为例,来说明我们自定义的通用分页组件的使用:
   业务层在用户业务接口UserService中提供的分页查询用户列表的方法queryUsers(Page page);

public List<User> queryUsers(Page page) throws ServiceException;

   调用者如果想查询第3页、每页查询15条用户信息,则可使用如下调用方式:

Page page = new Page(3, 15);
UserService userService = new UserServiceImpl();
List<User> usertList = userService.queryUser(page);

    这样调用者就取到了第3页15条的用户信息,而且此时的page对象中,已经包含了其他页面的信息,包括总页数,总数据量等,我们可根据这些信息来展示其他页码。
    如果只想取前5条数据,而不关心其他其他页面信息,则可使用:

Page page = Page.firstPage(5);
List<User> usertList = userService.queryUser(page);

   说明: 当不是分布式的应用时,前后台部署在同一个JVM上,这样通过前台传递的page对象的引用,后台设置好页面信息后,实际已经修改了前台传递的page对象,这样就不必再将page对象传回前台调用者。
   但是在分布式的环境中,后台在对page对象操作后,必须与结果集userList一起传递给调用者,并且Page类和PageScope类要实现序列化接口。

 

5. 测试:
   下面提供了一个简单的测试,来说明自定义的分页组件的使用正确性:

public class PageTest {

    /**
     * 测试构造一个仅表示第一页的分页对象。
     */
    @Test
    public void newFirstPage() {
        Page page = Page.newFirstPage(10);
        PageScope scope = page.getPageScope();

        assertTrue(page.isFirstPage());
        assertEquals(10, page.getTotalData());
        assertEquals(1, page.getTotalPage());
        assertEquals(1, page.getPageIndex());
        assertEquals(10, page.getPageSize());
        assertEquals(0, scope.getStartLine());
        assertEquals(9, scope.getEndLine());
    }

    /**
     * 测试构造一个指定当前页数和页面大小的分页对象。
     */
    @Test
    public void newPage() {
        Page page = new Page(2, 10);
        page.setTotalData(55);
        PageScope scope = page.getPageScope();

        assertFalse(page.isFirstPage());
        assertEquals(55, page.getTotalData());
        assertEquals(6, page.getTotalPage());
        assertEquals(2, page.getPageIndex());
        assertEquals(10, page.getPageSize());
        assertEquals(10, scope.getStartLine());
        assertEquals(19, scope.getEndLine());
    }
}

 

6. 相关说明:
   本文通过对实际项目中分页的需求,来构建一个通用的分页组件,以便在项目中复用并简化了项目中分页的开发工作。
   所有的源代码和测试代码已上传,仅供大家参考。
   如果有更好的建议和意见,还望大家不吝赐教,以便我改进,谢谢!
   在下文中我会再写一个自定义的Jsp分页标签,以便在页面中通用处理分页页码。

分享到:
评论
21 楼 深蓝luzlu 2011-10-22  
楼主辛苦,学习了
20 楼 xuyuanxi444 2011-07-21  
看了 还是不清楚 啊。。。。
19 楼 hnsyandy 2010-10-25  
不知道LZ的PageScope在此有何意义?
18 楼 finalerboy 2010-06-07  
我还想知道。

Action里面怎么拼参数,怎么取SIZE,DAO里怎么取总。。。

可否再详细一点?
17 楼 Wanghuidong 2010-05-29  
恩不错。。代码结构比较合理 借鉴一下
16 楼 pejuwe 2010-05-29  
还不错,看看!
15 楼 zhangshixi 2010-05-29  
<div class="quote_title">pengfeng 写道</div>
<div class="quote_div">没有看到楼主的分页组件里边包含当前页显示的数据,不知道楼主是怎么考虑当前页显示的数据的?</div>
<p><br>关于如何取得当前页面的数据,我在文章中已写的很清楚,请查看文章的<span style="color: #0000ff;">“如何使用”</span>部分。 <br>我也说过多遍,这里的分页只是提供一个供前后台方便交流使用的组件,至于如何处理当前页的数据、以及底层如何实现分页技术,都可根据具体实现的不同而不同,这样也大大提高了灵活和通用性,减少了代码的耦合性,给开发提供了便利的同时,也降低了代码的维护成本。</p>
<p> </p>
14 楼 pengfeng 2010-05-28  
没有看到楼主的分页组件里边包含当前页显示的数据,不知道楼主是怎么考虑当前页显示的数据的?
13 楼 zhangshixi 2010-05-28  
zhangqh_zz 写道
分页,程序部分例子很多,类似楼主的包装很多,但数据库部分如何处理也是很关键的
因为大量数据不可能一次取出,可能对于有些数据库有好的SQL,但有些可能没有
如DB2 只能row number()先生成序号,而oracle可能有自己的序号..........

这里的分页只是提供一个供前后台方便交流使用的组件,至于底层如何实现和优化,对于调用者来说应该是透明的吧。
12 楼 zhangqh_zz 2010-05-28  
分页,程序部分例子很多,类似楼主的包装很多,但数据库部分如何处理也是很关键的
因为大量数据不可能一次取出,可能对于有些数据库有好的SQL,但有些可能没有
如DB2 只能row number()先生成序号,而oracle可能有自己的序号..........
11 楼 jerry.yan.mj 2010-05-27  
精神可嘉!!!

但是这不是重复劳动吗?DisplayTag能够实现你的全部功能。
http://displaytag.sourceforge.net/1.2/index.html
10 楼 zhangshixi 2010-05-27  
dsea 写道
zhangshixi 写道
anzn20 写道
代码没给完!

代码我已打包上传上了,不知这位兄弟所说是何代码?

最好发布能直接导入的

兄弟,你也太懒了,我都一步一步写的那么详细了,你也就导入几个需要的jar包而已,我想这应该不是难事吧。如果我把所有都放上,势必给别人下载加大负担,如果你需要的话,可发信息给我,我发给你一份,请见谅。
9 楼 dsea 2010-05-27  
zhangshixi 写道
anzn20 写道
代码没给完!

代码我已打包上传上了,不知这位兄弟所说是何代码?

最好发布能直接导入的
8 楼 zhangshixi 2010-05-27  
anzn20 写道
代码没给完!

代码我已打包上传上了,不知这位兄弟所说是何代码?
7 楼 zhangshixi 2010-05-27  
xiangkun 写道
不知道 [下] 会不会有自定义的分页标签。。!!!
如果有的话,我期待看标签写的灵活性!

会有的,明天上...
6 楼 xiangkun 2010-05-27  
不知道 [下] 会不会有自定义的分页标签。。!!!
如果有的话,我期待看标签写的灵活性!
5 楼 anzn20 2010-05-27  
代码没给完!
4 楼 zhangshixi 2010-05-27  
naily 写道
lz讲解非常细致,清晰!赞一个

谢谢~希望通过分享,大家共同进步。
3 楼 naily 2010-05-27  
lz讲解非常细致,清晰!赞一个
2 楼 zhangshixi 2010-05-27  
caoyangx 写道
你看过springside吗?

只是大致了解,并未使用过,不知兄弟有何指教?

相关推荐

    通用分页组件

    通过理解以上知识点,我们可以更好地理解和构建一个通用分页组件,无论是在企业级应用还是个人项目中,都能提供更优质的数据浏览体验。对于提供的“分页组件”源码,进一步分析和学习将有助于提升开发技能,掌握更多...

    jsp分页组件(原创)

    综上所述,这个“jsp分页组件”是一个基于Java的开源工具,适用于JSP环境,提供分页功能的实现。开发者可以下载源码,研究其设计和实现,以便在自己的项目中复用或进行二次开发。通过阅读源码,不仅可以学习分页技术...

    FreeMarker通用的分页

    ### FreeMarker通用分页知识点解析 #### 1. 分页宏(Macro)定义 FreeMarker中的宏允许我们封装可重用的代码块,这在实现通用分页时显得尤为重要。宏`genPagination`是为分页而创建的核心组件,其参数包括: - `...

    通用查询组件 简单易用

    通用查询组件是软件开发中一个非常实用的功能模块,它的主要目标是提供一个灵活、便捷的查询界面,使得用户可以根据自己的需求自由设定查询条件,适用于各种数据表的检索操作。这样的组件通常具有高度可配置性和可...

    java通用分页含使用文档

    Java通用分页是一个常见且重要的技术点,尤其在开发大型数据集展示的Web应用时,分页能够有效地提高用户体验并优化服务器性能。本资源提供的"java通用分页含使用文档"是一个jar包,旨在简化Java Web开发中的分页实现...

    SSM 实现通用分页

    在这个项目中,"SSM实现通用分页"指的是通过这三个框架来实现数据的分页展示功能,这在大数据量的网页展示中尤为重要,可以有效提升用户体验并减轻服务器压力。 首先,Struts2作为MVC框架,负责处理HTTP请求和响应...

    Java 通用分页

    对于前端展示,我们可以使用模板引擎(如Thymeleaf、FreeMarker)或者前端框架(如Vue.js、React.js)配合后端提供的分页数据,动态渲染分页组件。 总结来说,Java实现通用分页涉及数据库查询、自定义分页类、封装...

    java web项目分页通用实现

    通过封装分页组件,我们可以将这些复杂逻辑模块化,便于在不同的项目中复用,从而提升开发质量和效率。这个DEMO正是为了帮助开发者理解并实践这一过程,无论你是初学者还是经验丰富的开发者,都能从中受益。

    分页组件ECSIDE文档

    ### 分页组件ECSIDE文档知识点详述 #### 一、ECSIDE组件概述与功能实现 **ECSIDE**,作为一款专为Web应用程序设计的分页组件,其核心功能在于提供高效、灵活的数据展示解决方案。该组件源于**eXtremeComponents**...

    (四)struts2- 2.3.15.3 spring3.2.4 mybatis-3.2.3 通用分页(不同数据库) 拦截器(2014-01-27 17:16)

    总的来说,这个项目展示了如何在Java Web环境中,利用Struts2、Spring和MyBatis三大框架,构建一个具备通用分页和拦截器功能的应用,为开发者提供了跨数据库的分页解决方案。通过学习和理解这个示例,开发者可以更好...

    整合了springMVC和通用分页以及通用mapper的框架

    它结合了SpringMVC、通用分页和通用Mapper三个核心组件,为Web应用程序提供了强大的数据访问和页面展示能力。 SpringMVC是Spring框架的一个模块,主要用于构建Web应用程序的模型-视图-控制器(MVC)架构。它提供了...

    asp.net 通用分页类

    `asp.net 通用分页类`就是为了解决这个问题而设计的,它允许开发者创建一个可复用的组件,以便在不同的数据展示页面上实现高效且灵活的分页功能。 首先,我们来看`C#`语言是如何实现分页逻辑的。在C#中,可以创建一...

    flex通用分页控件

    Flex通用分页控件是基于Adobe Flex框架开发的一种组件,用于在Web应用程序中实现数据的分页显示。Flex是一个开放源代码的、基于XML的编程语言,主要用于构建富互联网应用程序(Rich Internet Applications,RIA)。...

    通用分页js

    ### 通用分页JS知识点详解 #### 一、概述 在Web开发中,分页是一项常见且重要的功能,尤其在处理大量数据时更是必不可少。本文将深入解析一个名为“通用分页JS”的脚本,该脚本适用于JSP环境中,能够帮助开发者...

    通用分页实现及其OO设计探讨(2)

    通过对以上知识点的理解和应用,我们可以构建一个高效、灵活的通用分页解决方案,满足不同项目的需要。在实践中,结合具体的开发环境和工具,如JSP、Servlet、Spring Boot、MyBatis等,可以进一步细化和优化分页实现...

    JSP通用分页显示1.2.rar_java 分页显示_jsp_jsp 分页_jsp 分页_jsp分页

    **JSP分页显示技术详解...提供的"JSP通用分页显示1.2"示例是一个基础的实现,开发者可以在此基础上扩展和完善,以满足更复杂的需求。在学习和实践中,理解分页原理,熟练运用相关标签和技巧,是提升Web开发能力的关键。

    Reactjs实现通用分页组件的实例代码

    在ReactJS中实现一个通用的分页组件是一个常见的需求,特别是在构建数据驱动的Web应用时。这个实例代码提供了一个基于ReactJS的分页组件,它适用于各种场景,并且可以与Redux状态管理库配合使用。 首先,这个组件的...

    java通用分页源码

    这个“java通用分页”源码应该包含了上述部分或全部知识点的实现,对于学习者来说,可以通过阅读源码了解具体的实现细节,比如如何构建分页SQL,如何封装Page对象,以及如何与数据库交互。同时,也可以借此机会学习...

    Winform 通用分页控件

    【Winform 通用分页控件】 ...通过以上步骤,我们可以构建出一个功能齐全、易用且高效的Winform通用分页控件,满足各种数据展示需求。在实际项目中,这样的控件可以大大提升应用的用户体验和性能。

    很好的通用分页

    "很好的通用分页"是指一个高效且可复用的分页解决方案,它允许开发者轻松地在自己的应用中实现分页功能,只需要简单的复制和粘贴代码,大大提高了开发效率。 PageBean是Java Web开发中用于处理分页数据的一个对象,...

Global site tag (gtag.js) - Google Analytics