jsp分页技术实现

合集下载
相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

title: JSP分页技术实现
summary:使用工具类实现通用分页处理
author: evan_zhao
email
目前比较广泛使用的分页方式是将查询结果缓存在HttpSession或有状态bean中,翻页的时候从缓存中取出一页数据显示。

这种方法有两个主要的缺点:一是用户可能看到的是过期数据;二是如果数据量非常大时第一次查询遍历结果集会耗费很长时间,并且缓存的数据也会占用大量内存,效率明显下降。

其它常见的方法还有每次翻页都查询一次数据库,从ResultSet中只取出一页数据(使用();()获得总计录条数,使用()定位到本页起始记录)。

这种方式在某些数据库(如oracle)的JDBC实现中差不多也是需要遍历所有记录,实验证明在记录数很大时速度非常慢。

至于缓存结果集ResultSet的方法则完全是一种错误的做法。

因为ResultSet在Statement或Connection关闭时也会被关闭,如果要使ResultSet有效势必长时间占用数据库连接。

因此比较好的分页做法应该是每次翻页的时候只从数据库里检索页面大小的块区的数据。

这样虽然每次翻页都需要查询数据库,但查询出的记录数很少,网络传输数据量不大,如果使用连接池更可以略过最耗时的建立数据库连接过程。

而在数据库端有各种成熟的优化技术用于提高查询速度,比在应用服务器层做缓存有效多了。

在oracle数据库中查询结果的行号使用伪列ROWNUM表示(从1开始)。

例如select * from employee where rownum<10 返回前10条记录。

但因为rownum是在查询之后排序之前赋值的,所以查询employee按birthday排序的第100到120条记录应该这么写:
select * from (
select my_table.*, rownum as my_rownum f rom (
select name, birthday from employ ee order by birthday
) my_table where rownum <120
) where my_rownum>=100
mySQL可以使用LIMIT子句:
select name, birthday from employee order by birthday LIMIT 99,20 DB2有rownumber()函数用于获取当前行数。

SQL Server没研究过,可以参考这篇文章:
在Web程序中分页会被频繁使用,但分页的实现细节却是编程过程中比较麻烦的事情。

大多分页显示的查询操作都同时需要处理复杂的多重查询条件,sql语句需要动态拼接组成,再加上分页需要的记录定位、总记录条数查询以及查询结果的遍历、封装和显示,程序会变得很复杂并且难以理解。

因此需要一些工具类简化分页代码,使程序员专注于业务逻辑部分。

下面是我设计的两个工具类:
PagedStatement封装了数据库连接、总记录数查询、分页查询、结果数据封装和关闭数据库连接等操作,并使用了PreparedStatement支持动态设置参数。

RowSetPage参考PetStore的page by page iterator模式,设计RowSetPage 用于封装查询结果(使用OracleCachedRowSet缓存查询出的一页数据,关于使用CachedRowSet封装数据库查询结果请参考JSP页面查询显示常用模式)以及当前页码、总记录条数、当前记录数等信息,并且可以生成简单的HTML分页代码。

PagedStatement 查询的结果封装成RowsetPage。

下面是简单的使用示例:
1.
2.RowSet empRS = .RowSet) ();
3.if(empRS!=null) while() ) {
4.%>
5.<tr>
6.<td><%= ("EMP_ID")%></td>
7.<td><%= ("EMP_CODE")%></td>
8.<td><%= ("USER_NAME")%></td>
9.<td><%= ("REAL_NAME")%></td>
10.</tr>
11.<%
12.}etHTML("doQuery", "pageno")%></td>
13.</tr>
14.</table>
15.</form>
效果如图:
因为分页显示一般都会伴有查询条件和查询动作,页面应已经有校验查询条件和提交查询的javascript方法(如上面的doQuery),所以()生成的分页代码在用户选择新页码时直
接回调前面的处理提交查询的javascript方法。

注意在显示查询结果的时候上次的查询条件也需要保持,如<input type=text name=gender size=1 value="<%=("gender")%>">。

同时由于页码的参数名可以指定,因此也支持在同一页面中有多个分页区。

另一种分页代码实现是生成每一页的URL,将查询参数和页码作为QueryString附在URL 后面。

这种方法的缺陷是在查询条件比较复杂时难以处理,并且需要指定处理查询动作的servlet,可能不适合某些定制的查询操作。

如果对()生成的默认分页代码不满意可以编写自己的分页处理代码,RowSetPage提供了很多getter方法用于获取相关信息(如当前页码、总页数、总记录数和当前记录数等)。

在实际应用中可以将分页查询和显示做成jsp taglib,进一步简化JSP代码,屏蔽Java Code。

附:分页工具类的源代码,有注释,应该很容易理解。

继承Page)
继承PagedStatement)
您可以任意使用这些源代码,但必须保留author字样
1.
2.List;
3.import.ArrayList;
4.import.Collection;
5.import.Collections;
6.
7.
8./**
9.* Title: 分页对象<br>
10.* Description: 用于包含数据及分页信息的对象<br>
11.* Page类实现了用于显示分页信息的基本方
法,但未指定所含数据的类型,
12.* 可根据需要实现以特定方式组织数据的子类,
<br>
13.* 如RowSetPage以RowSet封装数据,ListPage
以List封装数据<br>
14.* Copyright: Copyright (c) 2002 <br>
15.* @author <br>
16.* @version
17.*/
18.public class Page implements.Serializable{
19.public static final Page EMPTY_PAGE = new Page();
20.public static final int DEFAULT_PAGE_SIZE = 20;
21.public static final int MAX_PAGE_SIZE = 9999;
22.
23.private int myPageSize = DEFAULT_PAGE_SIZE;
24.
25.private int start;
26.private int avaCount,totalSize;
27.private Object data;
28.
29.private int currentPageno;
30.private int totalPageCount;
31.
32./**
33.* 默认构造方法,只构造空页
34.*/
35.protected Page(){
36.(0,0,0,DEFAULT_PAGE_SIZE,new Object());
37.}
38.
39./**
40.* 分页数据初始方法,由子类调用
41.* @param start 本页数据在数据库中的起始位置
42.* @param avaCount 本页包含的数据条数
43.* @param totalSize 数据库中总记录条数
44.* @param pageSize 本页容量
45.* @param data 本页包含的数据
46.*/
47.protected void init(int start, int avaCount, int totalSiz
e, int pageSize, Object data){
48.
49.=avaCount;
50.= pageSize;
51.
52.= start;
53.= totalSize;
54.
55.=data;
56.
57.ength()<1) {
58.queryJSFunctionName = "gotoPage";
59.}
60.if(pageNoParamName == null|| ().length()<1){
61.pageNoParamName = "pageno";
62.}
63.
64.String gotoPage = "_"+queryJSFunctionName;
65.
66.StringBuffer html = new StringBuffer("\n");
67.("<script language=\"\">\n")
68..append("function ").append(gotoPage).append(
"(pageNo){ \n")
69..append( " var curPage=1; \n")
70..append( " try{ curPage = [\"")
71..append(pageNoParamName).append("\"].value;
\n")
72..append( " [\"").append(page
NoParamName)
73..append("\"].value = pageNo; \n")
74..append( " ").append(queryJS
FunctionName).append("(pageNo); \n")
75..append( " return true;
\n")
76..append( " }catch(e){ \n")
77.ppend( " try{ \n")
78.ppend( " [0].submit(); \n")
79.ppend( " }catch(e){ \n")
80..append( " alert('尚未定
义查询方法:function ")
81..append(queryJSFunctionName).append("()'); \n
")
82..append( " [\"").append(
pageNoParamName)
83..append("\"].value = curPage; \n")
84..append( " return false
; \n")
85.ppend( " } \n")
86..append( " } \n")
87..append( "}")
88..append( "</script> \n")
89..append( "");
90.( "<table border=0 cellspacing=0 cellpadding=0 al
ign=center width=80%> \n")
91..append( " <tr> \n")
92..append( " <td align=left><br> \n"
);
93.( " 共
").append( getTotalPageCount() ).append( "页")
94..append( " [") .append(getSta
rt()).append("..").append(getEnd())
95..append("/").append()).append("] \n")
96..append( " </td> \n")
97..append( " <td align=right> \n");
98.if(hasPreviousPage()){
99.( "[<a href='javascript:").append(gotoPage) 100..append("(") .append(getCurrentPageNo()-1) 101..append( ")'>上一页</a>] \n");
102.}
103.( " 第")
104..append( " <select name= '")
105..append(pageNoParamName).append("' onChange= 'javascript:")
106..append(gotoPage).append("'>\n");
107.String selected = "selected";
108.for(int i=1;i<=getTotalPageCount();i++){
109.if( i == getCurrentPageNo() )
110.selected = "selected";
111.else selected = "";
112.( " <option value='").append(i).ap pend("' ")
113..append(selected).append(">").append(i).app end("</option> \n");
114.}
115.if(getCurrentPageNo()>getTotalPageCount()){
116.( " <option value='").append(getCu rrentPageNo())
117..append("' selected>").append(getCurrentPageNo ())
118..append("</option> \n");
119.}
120.( " </select>页\n");
121.if(hasNextPage()){
122.( " [<a href='javascript:").append(g otoPage)
123..append("(").append((getCurrentPageNo()+1 ))
124..append( ")'>下一页</a>] \n"); 125.}
126.( "</td></tr></table> \n");
127.
128.return();
129.
130.}
131.}
132.
133.
134.
135.
136.RowSet;
137.
138.
139./**
140.* <p>Title: RowSetPage</p>
141.* <p>Description: 使用RowSet封装数据的分页对象</p>
142.* <p>Copyright: Copyright (c) 2003</p>
143.* @author
144.* @version
145.*/
146.
147.public class RowSetPage extends Page {
148.private.RowSet rs;
149.
150./**
151.*空页
152.*/
153.public static final RowSetPage EMPTY_PAGE = new RowSetP age();
154.
155./**
156.*默认构造方法,创建空页
157.*/
158.public RowSetPage(){
159.this(null, 0,0);
160.}
161.
162./**
163.*构造分页对象
164.*@param crs 包含一页数据的OracleCachedRowSet
165.*@param start 该页数据在数据库中的起始位置
166.*@param totalSize 数据库中包含的记录总数
167.*/
168.public RowSetPage(RowSet crs, int start, int totalSize) {
169.this(crs,start,totalSize,;
170.}
171.
172./**
173.*构造分页对象
174.*@param crs 包含一页数据的OracleCachedRowSet
175.*@param start 该页数据在数据库中的起始位置
176.*@param totalSize 数据库中包含的记录总数
177.*@pageSize 本页能容纳的记录数
178.*/
179.public RowSetPage(RowSet crs, int start, int totalSize, int pageSize) {
180.try{
181.int avaCount=0;
182.if(crs!=null) {
183.();
184.if()){
185.();
186.avaCount = ();
187.}
188.();
189.}
190.rs = crs;
191.(start,avaCount,totalSize,pageSize,rs); 192.}catch.SQLException sqle){
193.throw new RuntimeException());
194.}
195.}
196.
197./**
198.*取分页对象中的记录数据
199.*/
200.public.RowSet getRowSet(){
201.return rs;
202.}
203.
204.
205.}
206.
207.
208.
209.
210.BigDecimal;
211.import.List;
212.import.Iterator;
213.import.Collections;
214.
215.import.Connection;
216.import.SQLException;
217.import.ResultSet;
218.import.Statement;
219.import.PreparedStatement;
220.import.Timestamp;
221.import.RowSet;
222.
223./**
224.* <p>Title: 分页查询</p>
225.* <p>Description: 根据查询语句和页码查询出当页数据</p> 226.* <p>Copyright: Copyright (c) 2002</p>
227.* @author
228.* @version
229.*/
230.public abstract class PagedStatement {
231.public final static int MAX_PAGE_SIZE = ;
232.
233.protected String countSQL, querySQL;
234.protected int pageNo,pageSize,startIndex,totalCount; 235.protected.RowSet rowSet;
236.protected RowSetPage rowSetPage;
237.
238.private List boundParams;
239.
240./**
241.* 构造一查询出所有数据的PageStatement
242.* @param sql query sql
243.*/
244.public PagedStatement(String sql){
245.this(sql,1,MAX_PAGE_SIZE);
246.}
247.
248.
249./**
250.* 构造一查询出当页数据的PageStatement
251.* @param sql query sql
252.* @param pageNo 页码
253.*/
254.public PagedStatement(String sql, int pageNo){ 255.this(sql, pageNo, ;
256.}
257.
258./**
259.* 构造一查询出当页数据的PageStatement,并指定每页显示记录条数
260.* @param sql query sql
261.* @param pageNo 页码
262.* @param pageSize 每页容量
263.*/
264.public PagedStatement(String sql, int pageNo, int pageSi ze){
265.= pageNo;
266.= pageSize;
267.= (pageNo, pageSize);
268.= (new.LinkedList());
269.
270.= "select count(*) from ( "+ sql +") "; 271.= intiQuerySQL(sql, , pageSize);
272.}
273.
274.
275./**
276.*生成查询一页数据的sql语句
277.*@param sql 原查询语句
278.*@startIndex 开始记录位置
279.*@size 需要获取的记录数
280.*/
281.protected abstract String intiQuerySQL(String sql, int startIndex, int size);
282.
283.
284./**
285.*使用给出的对象设置指定参数的值
286.*@param index 第一个参数为1,第二个为2,。

287.*@param obj 包含参数值的对象
288.*/
289.public void setObject(int index, Object obj) throws SQL Exception{
290.BoundParam bp = new BoundParam(index, obj);
291.(bp);
292.( bp);
293.}
294.
295./**
296.*使用给出的对象设置指定参数的值
297.*@param index 第一个参数为1,第二个为2,。

298.*@param obj 包含参数值的对象
299.*@param targetSqlType 参数的数据库类型
300.*/
301.public void setObject(int index, Object obj, int target SqlType) throws SQLException{
302.BoundParam bp = new BoundParam(index, obj, target SqlType);
303.(bp);
304.(bp );
305.}
306.
307./**
308.*使用给出的对象设置指定参数的值
309.*@param index 第一个参数为1,第二个为2,。

310.*@param obj 包含参数值的对象
311.*@param targetSqlType 参数的数据库类型(常量定义在中)
312.*@param scale 精度,小数点后的位数
313.* (只对targetSqlType是或有效,其它类型则忽略)
314.*/
315.public void setObject(int index, Object obj, int target SqlType, int scale) throws SQLException{
316.BoundParam bp = new BoundParam(index, obj, target SqlType, scale) ;
317.(bp);
318.(bp);
319.}
320.
321./**
322.*使用给出的字符串设置指定参数的值
323.*@param index 第一个参数为1,第二个为2,。

324.*@param str 包含参数值的字符串
325.*/
326.public void setString(int index, String str)throws SQLEx ception{
327.BoundParam bp = new BoundParam(index, str) ; 328.(bp);
329.(bp);
330.}
331.
332./**
333.*使用给出的字符串设置指定参数的值
334.*@param index 第一个参数为1,第二个为2,。

335.*@param timestamp 包含参数值的时间戳
336.*/
337.public void setTimestamp(int index, Timestamp timestamp)t hrows SQLException{
338.BoundParam bp = new BoundParam(index, timestamp) ;
339.(bp);
340.( bp );
341.}
342.
343./**
344.*使用给出的整数设置指定参数的值
345.*@param index 第一个参数为1,第二个为2,。

346.*@param value 包含参数值的整数
347.*/
348.public void setInt(int index, int value)throws SQLExcept ion{
349.BoundParam bp = new BoundParam(index, new Integ er(value)) ;
350.(bp);
351.( bp );
352.}
353.
354./**
355.*使用给出的长整数设置指定参数的值
356.*@param index 第一个参数为1,第二个为2,。

357.*@param value 包含参数值的长整数
358.*/
359.public void setLong(int index, long value)throws SQLExce ption{
360.BoundParam bp = new BoundParam(index, new Long( value)) ;
361.(bp);
362.( bp );
363.}
364.
365./**
366.*使用给出的双精度浮点数设置指定参数的值
367.*@param index 第一个参数为1,第二个为2,。

368.*@param value 包含参数值的双精度浮点数
369.*/
370.public void setDouble(int index, double value)throws SQL Exception{
371.BoundParam bp = new BoundParam(index, new Doubl e(value)) ;
372.(bp);
373.( bp);
374.}
375.
376./**
377.*使用给出的BigDecimal设置指定参数的值
378.*@param index 第一个参数为1,第二个为2,。

379.*@param bd 包含参数值的BigDecimal
380.*/
381.public void setBigDecimal(int index, BigDecimal bd)throws SQLException{
382.BoundParam bp = new BoundParam(index, bd ) ;
383.(bp);
384.( bp);
385.}
386.
387.private void setParams(PreparedStatement pst) throws SQ LException{
388.if(pst==null|| ==null|| ) return;
389.BoundParam param;
390.for(Iterator itr =
391.param = (BoundParam) ();
392.if(param==null) continue;
393.if== .{
394., ;
395.}else{
396., , , ;
397.}
398.}
399.}
400.
401.
402.
403./**
404.* 执行查询取得一页数据,执行结束后关闭数据库连接
405.* @return RowSetPage
406.* @throws SQLException
407.*/
408.public RowSetPage executeQuery() throws SQLException{ 409."executeQueryUsingPreparedStatement");
410.Connection conn = ();
411.PreparedStatement pst = null;
412.ResultSet rs = null;
413.try{
414.pst = ;
415.setParams(pst);
416.rs =();
417.if()){
418.totalCount = (1);
419.} else{
420.totalCount = 0;
421.}
422.
423.();
424.();
425.
426.if(totalCount < 1 ) return;
427.
428.pst = ;
429.
430.;
431.setParams(pst);
432.rs =();
433.RowSet getRowSet(){
434.return;
435.}
436.
437.
438./**
439.*取封装成RowSetPage的查询结果
440.*@return RowSetPage
441.*/
442.public RowSetPage getRowSetPage() {
443.return;
444.}
445.
446.
447.
448./**
449.*关闭数据库连接
450.*/
451.public void close(){
452.;
453.}
454.
455.public BoundParam(int index, Object value, int s qlType) {
456.this(index, value, sqlType, 0);
457.}
458.
459.public BoundParam(int index, Object value, int s qlType, int scale) {
460.= index;
461.= value;
462.= sqlType;
463.= scale;
464.}
465.
466.public boolean equals(Object obj){
467.if(obj!=null&& ().isInstance(obj)){ 468.BoundParam bp = (BoundParam)obj; 469.if== return true;
470.}
471.return false;
472.}
473.}
474.
475.}
476.
477.
478.ResultSet;
479.import.SQLException;
480.import.RowSet;
481.import
482.
483./**
484.* <p>Title: 分页查询Oracle数据库实现</p>
485.* <p>Copyright: Copyright (c) 2002</p>
486.* @author
487.* @version
488.*/
489.public class PagedStatementOracleImpl extends PagedStatement { 490.
491./**
492.* 构造一查询出所有数据的PageStatement
493.* @param sql query sql
494.*/
495.public PagedStatementOracleImpl(String sql){
496.super(sql);
497.}
498.
499.
500./**
501.* 构造一查询出当页数据的PageStatement
502.* @param sql query sql
503.* @param pageNo 页码
504.*/
505.public PagedStatementOracleImpl(String sql, int pageNo){ 506.super(sql, pageNo);
507.}
508.
509./**
510.* 构造一查询出当页数据的PageStatement,并指定每页显示记录条数
511.* @param sql query sql
512.* @param pageNo 页码
513.* @param pageSize 每页容量
514.*/
515.public PagedStatementOracleImpl(String sql, int pageNo, i nt pageSize){
516.super(sql, pageNo, pageSize);
517.}
518.
519.
520./**
521.*生成查询一页数据的sql语句
522.*@param sql 原查询语句
523.*@startIndex 开始记录位置
524.*@size 需要获取的记录数
525.*/
526.protected String intiQuerySQL(String sql, int startIndex, int size){
527.StringBuffer querySQL = new StringBuffer();
528.if(size != {
529.("select * from (select my_table.*,rownum as my_rownum from(")
530..append( sql)
531..append(") my_table where ro wnum<").append(startIndex + size)
532..append(") where my_rownum>="
).append(startIndex);
533.} else{
534.("select * from (select my_table.*,rownum as my_rownum from(")
535..append(sql)
536..append(") my_table ") 537..append(") where my_rownum>="
).append(startIndex);
538.}
539.return();
540.}
541.
542./**
543.*将ResultSet数据填充进CachedRowSet
544.*/
545.protected RowSet populate(ResultSet rs) throws SQLExcep tion{
546.OracleCachedRowSet ocrs = new OracleCachedRowSet(); 547.(rs);
548.return ocrs;
549.}
550.
551.}。

相关文档
最新文档