title: JSP分页技术实现
summary:使用工具类实现通用分页处理
author: evan_zhao
email: evan_zhao@hotmail.com
目前比较广泛使用的分页方式是将查询结果缓存在HttpSession或有状态bean中,翻页的时候从缓存中取出一页数据显示。这种方法有两个主要的缺点:一是用户可能看到的是过期数据;二是如果数据量非常大时第一次查询遍历结果集会耗费很长时间,并且缓存的数据也会占用大量内存,效率明显下降。
其它常见的方法还有每次翻页都查询一次数据库,从ResultSet中只取出一页数据(使用rs.last();rs.getRow()获得总计录条数,使用rs.absolute()定位到本页起始记录)。这种方式在某些数据库(如oracle)的JDBC实现中差不多也是需要遍历所有记录,实验证明在记录数很大时速度非常慢。
至于缓存结果集ResultSet的方法则完全是一种错误的做法。因为ResultSet在Statement或Connection关闭时也会被关闭,如果要使ResultSet有效势必长时间占用数据库连接。
因此比较好的分页做法应该是每次翻页的时候只从数据库里检索页面大小的块区的数据。这样虽然每次翻页都需要查询数据库,但查询出的记录数很少,网络传输数据量不大,如果使用连接池更可以略过最耗时的建立数据库连接过程。而在数据库端有各种成熟的优化技术用于提高查询速度,比在应用服务器层做缓存有效多 了。
在oracle数据库中查询结果的行号使用伪列ROWNUM表示(从1开始)。例如 select * from employee where rownum<10 返回前10条记录。但因为rownum是在查询之后排序之前赋值的,所以查询employee按birthday排序的第100到120条记录应该这么写:
[pre] select * from (
select my_table.*, rownum as my_rownum from (
select name, birthday from employee order by birthday
) my_table where rownum <120
) where my_rownum>=100
[/pre]
mySQL可以使用LIMIT子句:
select name, birthday from employee order by birthday LIMIT 99,20
DB2有rownumber()函数用于获取当前行数。
SQL Server没研究过,可以参考这篇文章:http://www.csdn.net/develop/article/18/18627.shtm
在Web程序中分页会被频繁使用,但分页的实现细节却是编程过程中比较麻烦的事情。大多分页显示的查询操作都同时需要处理复杂的多重查询条件,sql语句需要动态拼接组成,再加上分页需要的记录定位、总记录条数查询以及查询结果的遍历、封装和显示,程序会变得很复杂并且难以理解。因此需要一些工具类简化分页代码,使程序员专注于业务逻辑部分。下面是我设计的两个工具类:
PagedStatement 封装了数据库连接、总记录数查询、分页查询、结果数据封装和关闭数据库连接等操作,并使用了PreparedStatement支持动态设置参数。
RowSetPage 参考PetStore的page by page iterator模式, 设计RowSetPage用于封装查询结果(使用OracleCachedRowSet缓存查询出的一页数据,关于使用CachedRowSet封装数据库查询结果请参考JSP页面查询显示常用模式)以及当前页码、总记录条数、当前记录数等信息, 并且可以生成简单的HTML分页代码。
PagedStatement 查询的结果封装成RowsetPage。
下面是简单的使用示例:
-
- //DAO查询数据部分代码:
- …
- public RowSetPage getEmployee(String gender, int pageNo) throwsException{
- String sql="select emp_id, emp_code, user_name, real_name from employee where gender =?";
- //使用Oracle数据库的分页查询实现,每页显示5条
- PagedStatement pst =new PagedStatementOracleImpl(sql, pageNo, 5);
- pst.setString(1, gender);
- return pst.executeQuery();
- }
-
-
- //Servlet处理查询请求部分代码:
-
- …
- int pageNo;
- try{
- //可以通过参数pageno获得用户选择的页码
- pageNo = Integer.parseInt(request.getParameter("pageno") );
- }catch(Exception ex){
- //默认为第一页
- pageNo=1;
- }
- String gender = request.getParameter("gender" );
- request.setAttribute("empPage", myBean.getEmployee(gender, pageNo) );
- …
-
- //JSP显示部分代码
- <%@ page import = "page.RowSetPage"%>
- …
- <script language="javascript">
- function doQuery(){
- form1.actionType.value="doQuery";
- form1.submit();
- }
- </script>
- …
- <form name=form1 method=get>
- <input type=hidden name=actionType>
- 性别:
- <input type=text name=gender size=1 value="<%=request.getParameter("gender")%>">
- <input type=button value=" 查询 " onclick="doQuery()">
- <%
- RowSetPage empPage = (RowSetPage)request.getAttribute("empPage");
- if (empPage == null ) empPage = RowSetPage.EMPTY_PAGE;
- %>
- …
- <table cellspacing="0" width="90%">
- <tr><td>ID</td><td>代码</td><td>用户名</td><td>姓名</td> </tr>
- <%
- javax.sql.RowSet empRS = (javax.sql.RowSet) empPage.getRowSet();
- if (empRS!=null) while (empRS.next() ) {
- %>
- <tr>
- <td><%= empRS.getString("EMP_ID")%></td>
- <td><%= empRS.getString("EMP_CODE")%></td>
- <td><%= empRS.getString("USER_NAME")%></td>
- <td><%= empRS.getString("REAL_NAME")%></td>
- </tr>
- <%
- }// end while
- %>
- <tr>
- <%
- //显示总页数和当前页数(pageno)以及分页代码。
- //此处doQuery为页面上提交查询动作的javascript函数名, pageno为标识当前页码的参数名
- %>
- <td colspan=4><%= empPage .getHTML("doQuery", "pageno")%></td>
- </tr>
- </table>
- </form>
效果如图:
因为分页显示一般都会伴有查询条件和查询动作,页面应已经有校验查询条件和提交查询的javascript方法(如上面的doQuery),所以 RowSetPage.getHTML()生成的分页代码在用户选择新页码时直接回调前面的处理提交查询的javascript方法。注意在显示查询结果的时候上次的查询条件也需要保持,如<input type=text name=gender size=1 value="<%= request.getParameter("gender")%>">。同时由于页码的参数名可以指定,因此也支持在同一页面中有多个分页区。
另一种分页代码实现是生成每一页的URL,将查询参数和页码作为QueryString附在URL后面。这种方法的缺陷是在查询条件比较复杂时难以处理,并且需要指定处理查询动作的servlet,可能不适合某些定制的查询操作。
如果对RowSetPage.getHTML()生成的默认分页代码不满意可以编写自己的分页处理代码,RowSetPage提供了很多getter方法用于获取相关信息(如当前页码、总页数、 总记录数和当前记录数等)。
在实际应用中可以将分页查询和显示做成jsp taglib, 进一步简化JSP代码,屏蔽Java Code。
附:分页工具类的源代码, 有注释,应该很容易理解。
1.Page.java
2.RowSetPage.java(RowSetPage继承Page)
3.PagedStatement.java
4.PagedStatementOracleImpl.java(PagedStatementOracleImpl继承PagedStatement)
您可以任意使用这些源代码,但必须保留author evan_zhao@hotmail.com字样
-
- ///////////////////////////////////
- //
- // Page.java
- // author: evan_zhao@hotmail.com
- //
- ///////////////////////////////////
-
- package page;
-
- import java.util.List;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Collections;
-
-
- /**
- * Title: 分页对象<br>
- * Description: 用于包含数据及分页信息的对象<br>
- * Page类实现了用于显示分页信息的基本方法,但未指定所含数据的类型,
- * 可根据需要实现以特定方式组织数据的子类,<br>
- * 如RowSetPage以RowSet封装数据,ListPage以List封装数据<br>
- * Copyright: Copyright (c) 2002 <br>
- * @author evan_zhao@hotmail.com <br>
- * @version 1.0
- */
- public class Page implements java.io.Serializable {
- publicstaticfinal Page EMPTY_PAGE = new Page();
- publicstaticfinalint DEFAULT_PAGE_SIZE = 20;
- publicstaticfinal int MAX_PAGE_SIZE = 9999;
-
- privateint myPageSize = DEFAULT_PAGE_SIZE;
-
- privateint start;
- privateint avaCount,totalSize;
- privateObject data;
-
- privateint currentPageno;
- privateint totalPageCount;
-
- /**
- * 默认构造方法,只构造空页
- */
- protected Page(){
- this.init(0,0,0,DEFAULT_PAGE_SIZE,newObject());
- }
-
- /**
- * 分页数据初始方法,由子类调用
- * @param start 本页数据在数据库中的起始位置
- * @param avaCount 本页包含的数据条数
- * @param totalSize 数据库中总记录条数
- * @param pageSize 本页容量
- * @param data 本页包含的数据
- */
- protectedvoid init(int start, int avaCount, int totalSize, int pageSize, Object data){
-
- this.avaCount =avaCount;
- this.myPageSize = pageSize;
-
- this.start = start;
- this.totalSize = totalSize;
-
- this.data=data;
-
- //System.out.println("avaCount:"+avaCount);
- //System.out.println("totalSize:"+totalSize);
- if (avaCount>totalSize) {
- //throw new RuntimeException("记录条数大于总条数?!");
- }
-
- this.currentPageno = (start -1)/pageSize +1;
- this.totalPageCount = (totalSize + pageSize -1) / pageSize;
-
- if (totalSize==0 && avaCount==0){
- this.currentPageno = 1;
- this.totalPageCount = 1;
- }
- //System.out.println("Start Index to Page No: " + start + "-" + currentPageno);
- }
-
- public Object getData(){
- returnthis.data;
- }
-
- /**
- * 取本页数据容量(本页能包含的记录数)
- * @return 本页能包含的记录数
- */
- publicint getPageSize(){
- returnthis.myPageSize;
- }
-
- /**
- * 是否有下一页
- * @return 是否有下一页
- */
- publicboolean hasNextPage() {
- /*
- if (avaCount==0 && totalSize==0){
- return false;
- }
- return (start + avaCount -1) < totalSize;
- */
- return (this.getCurrentPageNo()<this.getTotalPageCount());
- }
-
- /**
- * 是否有上一页
- * @return 是否有上一页
- */
- publicboolean hasPreviousPage() {
- /*
- return start > 1;
- */
- return (this.getCurrentPageNo()>1);
- }
-
- /**
- * 获取当前页第一条数据在数据库中的位置
- * @return
- */
- publicint getStart(){
- return start;
- }
-
- /**
- * 获取当前页最后一条数据在数据库中的位置
- * @return
- */
- publicint getEnd(){
- int end = this.getStart() + this.getSize() -1;
- if (end<0) {
- end = 0;
- }
- return end;
- }
-
- /**
- * 获取上一页第一条数据在数据库中的位置
- * @return 记录对应的rownum
- */
- publicint getStartOfPreviousPage() {
- returnMath.max(start-myPageSize, 1);
- }
-
-
- /**
- * 获取下一页第一条数据在数据库中的位置
- * @return 记录对应的rownum
- */
- publicint getStartOfNextPage() {
- return start + avaCount;
- }
-
- /**
- * 获取任一页第一条数据在数据库中的位置,每页条数使用默认值
- * @param pageNo 页号
- * @return 记录对应的rownum
- */
- publicstaticint getStartOfAnyPage(int pageNo){
- return getStartOfAnyPage(pageNo, DEFAULT_PAGE_SIZE);
- }
-
- /**
- * 获取任一页第一条数据在数据库中的位置
- * @param pageNo 页号
- * @param pageSize 每页包含的记录数
- * @return 记录对应的rownum
- */
- publicstaticint getStartOfAnyPage(
相关推荐
总结来说,"jsp分页插件(内附源码)"是一个方便开发者的工具,能够快速地在JSP项目中实现高效且美观的分页功能。通过学习和使用这个插件,开发者不仅可以提升开发效率,还能深入理解分页技术,为项目开发带来便利。
\ch18\* 第18章 JSP页面分页技术实现所有例程代码 \ch19\* 第19章 上传下载文件模块所有例程代码 \ch20\* 第20章 使用JSP发送邮件所有例程代码 \ch21\* 第21章 异常及日志模块所有例程代码 \ch22\* 第22章 保护...
在本文中,我们将探讨一种实现简单无刷新分页的方法,这是一种在不重新加载整个网页的情况下更新内容的技术,提高用户体验。我们将分析服务器端的模拟数据生成以及客户端的JavaScript代码实现。 一、服务器端模拟...
通过以上步骤,我们可以成功地利用JQuery、Ajax与JSON技术实现了一个简单的分页显示功能。这种方法不仅提高了用户体验,还减轻了服务器的压力。在未来,随着Web技术的发展,这种技术的应用场景将会更加广泛。
四、技术实现细节 1. 数据库设计:创建用户表(User)和消息表(Message),包含字段如用户ID、用户名、消息ID、发件人ID、收件人ID、消息内容、发送时间等。 2. 表单提交:JSP页面上的表单元素(如文本框、按钮)...
【基于jsp+servlet实现的简单博客系统实例】是一个适合初学者理解Web开发基础的项目,主要涉及的技术栈是JavaServer Pages(JSP)和Servlet。在这个实例中,开发者使用这两种技术构建了一个基本的博客系统,提供了...
基于EJB的真实世界模型,附源代码,部分功能需JSP配合完成。 J2ME优化压缩PNG文件 4个目标文件 内容索引:JAVA源码,综合应用,J2me游戏,PNG,图形处理 这是个J2ME控制台程序,它能剔除PNG文件中的非关键数据段,减少...
标题中的“基于Java的实例源码-毕业论文:搜索引擎系统附源代码.zip”指的是一个包含Java编程语言实现的搜索引擎系统的毕业设计项目。这个项目可能是为了展示如何构建一个基本的搜索引擎,帮助用户在特定数据集或者...
【标题】"bbs项目源码(java...同时,通过阅读源码,可以学习到如何设计和实现论坛的用户认证、权限控制、分页显示、搜索功能等常见模块。对于想要提升自己在Java和MySQL领域技能的开发者来说,这是一个极好的实战项目。
首先,JSP是Java的一种动态网页技术,它允许开发者在HTML页面中嵌入Java代码,从而实现服务器端的数据处理和动态内容生成。在博客阅读器中,JSP可能会用来处理用户的请求,如登录、搜索博客、获取博客文章等,同时与...
1.本书1~16章所附代码的运行环境 操作系统:Windows 2003、Windows XP Professional,或者Windows 2000 开发环境:Microsoft Visual Studio 2005 数据库:SQL Server 2005 Web服务器:IIS 5.1及以上版本 2....
【JavaWeb期刊管理系统】是一个基于JavaWeb技术实现的在线期刊管理平台,主要目的是为了帮助学生在课程设计中理解和掌握Web应用程序开发的相关技能。这个系统涵盖了用户管理、期刊投稿、审稿流程、发表与检索等功能...
涵盖了MVC设计模式、XML、分页、性能优化、结构化查询语言(SQL)、多线程、文件加密、软件生命周期、网络协议、数据结构、并发编程、EJB生命周期、GUI设计、事件处理、Applet安全性和通信方式、逻辑运算与条件运算...
本课题利用JSP技术和JDBC数据库技术,以MyEclipse为开发工具,基于B/S模式设计并实现了酒店预定系统。本系统可以方便游客预定酒店,实时了解酒店资源信息,避免了酒店资源重复预定的不足,极大提高了酒店管理的效率...
而"科研成果申报管理系统源码"则包含了所有源代码,包括实体类(Entity)、服务接口(Service)、服务实现(ServiceImpl)、控制器(Controller)、视图(JSP或HTML)、以及配置文件等。 学习和研究这个项目,...
本书内容丰富、技术全面、案例实用,而且所有的实例都以MyEclipse工程的形式组织,并按章节的顺序组织在附书光盘中,源代码工程都经过精心调试,可以直接导入MyEclipse中运行。 本书内容精练、重点突出、实例丰富,...
- 分页通常通过分页标签或自定义的分页类实现,根据查询结果的总数量和每页显示的记录数来计算页码,并在请求中携带当前页码来获取对应数据。 6. **什么是黑盒测试和白盒测试?** - 黑盒测试关注的是程序的功能,...
"aa.rar_Javaweb 查找"这个压缩包可能包含了实现这一功能的源代码示例,让我们来详细探讨一下相关知识点。 1. **Servlet与JSP**: - **Servlet**:JavaWeb应用中的核心组件,用于接收客户端请求,处理数据,然后...
继承是子类继承父类的属性和方法,实现代码重用。多态允许一个接口调用不同的实现,提高代码的灵活性和扩展性。 【Servlet生命周期】 Servlet的生命周期包括加载和实例化、初始化、服务、销毁四个阶段。Servlet和...