`
seara
  • 浏览: 639909 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

用Session和唯一索引字段实现通用Web分页功能

阅读更多
本文为原创,如需转载,请注明作者和出处,谢谢!
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="ProgId" content="Word.Document"> <meta name="Generator" content="Microsoft Word 11"> <meta name="Originator" content="Microsoft Word 11"> <link rel="File-List" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml"> <!--[if gte mso 9]><xml> Normal 0 7.8 磅 0 2 false false false MicrosoftInternetExplorer4 </xml><![endif]--><!--[if gte mso 9]><![endif]--><style> <!-- /* Font Definitions */ &#64;font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1;} &#64;font-face {font-family:""&#64;宋体"; panose-1:2 1 6 0 3 1 1 1 1 1;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; font-size:10.5pt; font-family:"Times New Roman";} /* Page Definitions */ &#64;page {} &#64;page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt;} div.Section1 {page:Section1;} --> </style> <!--[if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable { mso-style-parent:""; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman";} </style> <![endif]-->

Web系统虽然现在很流行,但是分页问题一直长期困扰着Web系统的开发人员。对于不同的数据库,可能开发人员对分页的处理分有很大差别。个人认为,使用MySQL开发Web系统的程序员是感到最舒服的,因为,在MySQL中提供了limit语句,可以获得查询结果的一段数据。如下面的SQL语句所示:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->select*fromtable1limit1,20

<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="ProgId" content="Word.Document"><meta name="Generator" content="Microsoft Word 11"><meta name="Originator" content="Microsoft Word 11"><link rel="File-List" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml"><!--[if gte mso 9]><xml> Normal 0 7.8 磅 0 2 false false false MicrosoftInternetExplorer4 </xml><![endif]--><!--[if gte mso 9]><![endif]--><style> <!-- /* Font Definitions */ &#64;font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1;} &#64;font-face {font-family:""&#64;宋体"; panose-1:2 1 6 0 3 1 1 1 1 1;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; font-size:10.5pt; font-family:"Times New Roman";} /* Page Definitions */ &#64;page {} &#64;page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt;} div.Section1 {page:Section1;} --> </style><!--[if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable { mso-style-parent:""; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman";} </style> <![endif]-->

上面的SQL表示从table1中查出记录,并返回从第2条开始的20条记录(第1条记录从0开始)。

对于其他的数据库,恐怕就没MySQL那么容易查询出记录段了。在SQL Server2005中也提供了类似MySQL的处理方法(可以使用ROW_NUMBER()函数来实现这个功能),SQL语句如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->WithtAS
(
SELECTcontactid,namestyle,lastname,
ROW_NUMBER()
over(orderbynamestyle)asRowNumber
FROMPerson.Contact
)
select*fromt
WhereRowNumberBetween20and30

<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="ProgId" content="Word.Document"><meta name="Generator" content="Microsoft Word 11"><meta name="Originator" content="Microsoft Word 11"><link rel="File-List" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml"><!--[if gte mso 9]><xml> Normal 0 7.8 磅 0 2 false false false MicrosoftInternetExplorer4 </xml><![endif]--><!--[if gte mso 9]><![endif]--><style> <!-- /* Font Definitions */ &#64;font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1;} &#64;font-face {font-family:""&#64;宋体"; panose-1:2 1 6 0 3 1 1 1 1 1;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; font-size:10.5pt; font-family:"Times New Roman";} /* Page Definitions */ &#64;page {} &#64;page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt;} div.Section1 {page:Section1;} --> </style><!--[if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable { mso-style-parent:""; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman";} </style> <![endif]-->

虽然上面的SQL语句虽然也可以实现和MySQL一样的功能,但却比MySQLlimit复杂一些。

如果在数据库中提供了实现Web分页的机制,就算复杂一些,也是可以解决的。但有效数据库可能并未提供这种机制。这就得使用更复杂的方法来实现Web分页,如在SQL Server2000中未提供ROW_NUMBER()函数,就有很多开发人员通过编写分页的存储过程来处理。这样做既复杂,又不通用。假设要移植到Oracle上,还得费一番功夫。

在本文给出一种直接使用Web中的Session对象来方式来实现分页的功能,Session是在Web系统中保存当前分话数据的。我们可以想象。分页的难点在哪里,就象MySQL中的limit语句一样,只需要有两个值:起使记录数和要获得的记录总数就可以了。要获得的记录总数这个我们很容易知道,一般就是分一页的记录数。但是起使记录数却很难获得。

如果使用自增键当然可以,但这要建立在表只增不删,而且id1或一个已知的起始位置开始的情况。如果删除了表中的一些数据,自增键就不再是从1n,依次递增了。也就是中间可能有空档。如自增键从20100,中间可能只有10条记录。因此,单纯使用自增键并不能很好地解决分页问题。

但却可以将Session和自增键组合来解决分页问题。大家可以设想,在用户第一次查询时,如select * from table1 where field1 like '%abc%',这时将记录全部查出。假设每页显示50条记录,这时可以从头开始取出50条记录。这不会有任何问题。然后,当用户要查看第2页时,最普通的做是再执行一次上面的SQL语句,然后从第51第记录开始,再取出50条记录。如果这样做,将大大浪费服务器的资源。

为了解决这个问题,可以在每一次执行完上面的SQL语句后,除了取出前50条记录外,再通过记录的定位,将其他页面的起始id值保存在Session中(可以放在List对象中)。然后在用户要查看第2页或后面的页时,直接从Session中取出该页起始id的值,如果使用的是SQL Servlet数据库,可以使用top n,其中n表示每页记录数,来查询当前页的记录。

先拿Java为例来说明一下。下面的代码在Session中记录了第一页到最后一页的起始id:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->//rs为记录集,其他语言的操作类似
ResultSetrs=stmt.executeQuery("select*fromtable1wherefield1like'%abc%'");
intn=1;
while(rs.absolute(n))
{
intid=rs.getInt(id)
//将id保存在Session中
n+=50;
}

<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="ProgId" content="Word.Document"><meta name="Generator" content="Microsoft Word 11"><meta name="Originator" content="Microsoft Word 11"><link rel="File-List" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml"><!--[if gte mso 9]><xml> Normal 0 7.8 磅 0 2 false false false MicrosoftInternetExplorer4 </xml><![endif]--><!--[if gte mso 9]><![endif]--><style> <!-- /* Font Definitions */ &#64;font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1;} &#64;font-face {font-family:""&#64;宋体"; panose-1:2 1 6 0 3 1 1 1 1 1;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; font-size:10.5pt; font-family:"Times New Roman";} /* Page Definitions */ &#64;page {} &#64;page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt;} div.Section1 {page:Section1;} --> </style><!--[if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable { mso-style-parent:""; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman";} </style> <![endif]-->

从上面的代码可以看出,使用ResultSetabsolute来定位记录,并取出当前记录的id值(一个自增字段),并将其保存在Session中。

假设共查询出500条记录,那么Session中保存的id值有可能是下面的样子:

151123179229290367567699

从上面的id值可以看出,中间有断档。但这9id值之间的记录数都是50个。如下面的SQL语句将查询出50个记录:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->select*fromtable1wherefield1like'%abc%'and(id>=290andid<367)

假设用户要查看第3页的话,就会取出123179,并将其加入select 语句的where条件,类似上面的SQL语句。这样用户除了第一次查询外,查看其他页都会只返回当前页面的记录了。

上面的方法还有一些问题,如当第一次返回的记录很多的话,使用absolute方法进行循环所有的记录可能有些慢,那可以在程序中做个约定,只循环41次,也就是保存前40页的记录,当用户要查看第41页的话,再取出第40页的开始记录的id值,将再次查询从该id值往后的所有记录,再记录40页的id值,也就是这时已经有80页的id记录被保存在Session中的。以此类推,

当然,这种方法也不可避免地遇到删除记录的情况,如果用户正在查看页面,这时某一页的记录被删除了,当用户再次要查看这页时,根据Session中保存的id区间,就会得到少于50的记录。在这种情况下,如果使用的是SQL Servlet,就好办一些,可以在where条件中只加id的上限,不加下限,然后使用top关键字来限制查询出的记录数,SQL语句如下:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->selecttop50*fromtable1wherefield1like'%abc%'andid>=290

如果使用的是其他数据库,没有类型top的关键字,可以在查询时多加一个区间,如用户要查询第2页的数据,可以将第2页和第3页的都查出来,这样一般就可以获得超过50条的记录。但如果记录数还不够(这个表的记录被删除的太多了),笔者建议重新查询所有的记录,重新更新一下Session对象中的id值。

总之,本算法就是在第一次查询时预先将后面页面的起始记录的id值事先保存起来,然后等待以后查看其他页面时使用。如果这时某个页面的记录被删除(如果当前页面记录数不足页面记录总数,被示为有记录删除),可以重新更新一下Session中的id值,然后根据新的id值再查一遍。但要注意的是这个id值最好使用数据库的自增型字段(一般的数据库,甚至桌面数据库都会有自增型字段类型)。为了尽量避免总更新Session中的id值,可以在查询一个页面时查询出两个页面的记录,这样在一般情况下,会保证记录数超过页面记录总数。但这样做一个缺点,就是可能两个相邻页面的记录有一定的重复。不过并没有太大影响。我们在网上看某些论坛的贴子时,有时可能也会发现两个相邻页面的记录有重复。

本分页方法适合于所有的数据库,无论是网络数据库(OracleSQL ServletDB2等),以及桌面数据库(accessparadoxpdf等)。并且不需要在数据库中建立额外的资源,如存储过程等。(当然,每个表需要有一个自增类型字段,这一点很关键)。

补充一下,这种方法只适合于一个排序字段的查询,而且这个排序字段值不能有重复的,也就是说得是有唯一索引的字段。在本文中使用了自增键来说明,但也可以 是其他字段,如不重复的时间字段,按时间排序后。可以使用本文的方法。而且唯一字段区间值也可以使用其他的方式保存,如viewstate,hide input等。

哪位读者有更好,更通用的分页方法(最好不要在数据库中建立象存储过程一样的资源,尽量不要使用与数据库相关的语句,如SQL Server中的top),请跟贴。



国内最棒的Google Android技术社区(eoeandroid),欢迎访问!

《银河系列原创教程》发布

《Java Web开发速学宝典》出版,欢迎定购

分享到:
评论

相关推荐

    hibernate中实现真分页和假分页技术

    1. 分页查询时,应避免在`WHERE`子句中使用不稳定的排序条件,否则可能导致重复数据或分页错乱。 2. 如果数据库支持,尽量使用索引来优化分页查询,尤其是在`ORDER BY`字段上。 3. 对于大数据量的分页,真分页通常...

    asp.net实现文章分页

    在ASP.NET中,实现文章分页是Web应用中常见的需求,尤其对于内容丰富的网站,如博客、论坛等,分页可以有效地提高用户体验,避免一次性加载过多数据导致页面响应慢。以下将详细介绍如何在ASP.NET中实现文章分页,并...

    Struts2+HIBERNATE实现分页(完整讲解)

    在这个教程中,我们将探讨如何利用Struts2和Hibernate实现分页功能。 **1. 数据库设计** 首先,我们需要一个简单的数据库表,如Tb_soft中的software表,包含一些基本字段如Tsoftware, fSoftname, fListImage等。...

    购物车(包括分页)

    6. **SQL查询优化**:在Sqlserver数据库中,针对分页查询,可能需要使用`OFFSET-FETCH`或`ROW_NUMBER()`等方法,同时考虑索引的建立,以提高查询性能。 7. **安全性**:确保购物车数据的安全性,例如防止SQL注入...

    注册登录分页

    在IT行业中,"注册登录分页"是一个常见的功能模块,主要涉及到用户管理、身份验证以及数据展示等核心技术。下面将详细阐述这个主题所涵盖的知识点。 首先,**注册**是用户获取系统权限的第一步。注册功能通常需要...

    基于hiberate的分页小例子

    总结,这个“基于Hibernate的分页小例子”涵盖了如何在Java Web应用中使用Hibernate与MySQL数据库进行分页查询的基本步骤,同时提到了一些性能优化的策略。通过学习和实践这个例子,开发者能够更好地理解和运用...

    JAVA数字分页案例

    在Java开发中,分页是Web应用程序中非常常见的一种功能,它主要用于处理大量数据时,提高用户体验,避免一次性加载所有数据导致页面响应慢或者内存压力过大。本案例以"JAVA数字分页案例"为主题,结合SpringMVC和...

    asp完美分页

    在ASP中,可以通过ADO(ActiveX Data Objects)来处理这种关系,使用JOIN操作连接主表和从表,实现数据的查询和分页。 `BreakPageSQL`可能是用于SQL版本的分页脚本,它可能包含SQL查询语句以及如何计算总页数和当前...

    struts2+pager-taglib 实现分页小例子

    在Action类中,我们可以使用Hibernate的Session对象来执行SQL查询,例如`select * from users limit #{start}, #{count}`,其中#{start}是当前页第一条记录的索引,#{count}是每页的记录数。这样,我们就能获取到...

    hibernate分页查询

    1. 使用索引:确保用于分页的关键字段(如排序字段)有合适的索引,以提高查询速度。 2. 避免全表扫描:尽可能减少查询的数据量,避免一次性加载大量数据。 3. 考虑缓存:合理利用Hibernate的缓存机制,减少对数据库...

    hibernate+struts后台分页

    1. 使用索引:确保用于排序和分页的字段有适当的数据库索引,以加快查询速度。 2. 数据库级别的分页:某些数据库系统提供了原生的分页功能,比如MySQL的LIMIT和OFFSET,这通常比在应用程序层面实现更有效率。 3. ...

    数据库分页代码

    数据库分页是Web应用开发中常见的一种技术,用于在大量数据中实现高效的浏览体验,避免一次性加载所有数据导致页面响应慢或内存压力过大。这里我们将深入探讨如何在Java环境下实现数据库分页,并结合UTF-8编码标准...

    基于SSH框架的分页示例

    在Hibernate中,可以使用`Criteria`、`HQL`(Hibernate Query Language)或`SQL`配合`Query.setFirstResult()`和`Query.setMaxResults()`方法实现分页。`setFirstResult()`指定从哪一条记录开始,`setMaxResults()`...

    JavaWeb_jsp分页技术实例

    在这个"JavaWeb_jsp分页技术实例"中,我们将深入探讨如何在JavaWeb应用中实现分页功能,特别是使用JSP(JavaServer Pages)进行开发。 分页通常涉及到两个主要部分:前端展示和后端处理。前端负责展示当前页的数据...

    通讯录项目 jsp的分页技术

    综上所述,"通讯录项目"利用JSP的分页技术,有效地处理了大量联系人的展示问题,这不仅提升了用户在查看和管理通讯录时的体验,也展示了Web开发中数据处理和页面动态化的重要实践。通过深入理解并应用这些技术,...

    Nhibernate分页

    对于分页查询中用到的排序字段,确保在数据库层面创建了索引,可以大大提高查询效率。 4. **避免全表扫描** 在设计查询条件时,尽可能避免全表扫描,利用索引和查询条件来限制返回的数据量。 **四、总结** ...

    struts分页技术

    6. **拦截器(Interceptor)**:在Struts 2中,可以利用拦截器实现通用的分页逻辑,比如在请求处理之前,自动计算出当前页的数据并设置到Action上下文中。 7. **国际化(i18n)**:分页的“上一页”、“下一页”等...

    jsp+servlet分页

    同时,实际开发中还需要了解如何使用数据库查询语句(如SQL的LIMIT和OFFSET)实现分页,以及可能用到的各种框架的分页API。通过这些技术,我们可以有效地管理和展示大量数据,提高用户的浏览体验。

    Hibernate 分页查询效果

    在IT行业中,数据库查询是日常开发中的重要环节,特别是在数据量庞大的...在实际应用中,开发者应根据项目需求和数据规模选择合适的分页策略,同时结合其他优化手段,如数据库索引、缓存技术等,来提升整体系统性能。

    java分页汇总技术文档下载

    - 使用索引:确保用于排序和分页的字段有合适的索引,提升查询速度。 综上所述,Java分页技术有多种实现方式,选择哪种取决于项目的技术栈、性能需求和数据规模。了解这些技术并熟练应用,能有效提高项目的开发效率...

Global site tag (gtag.js) - Google Analytics