title: JSP分页技术实现 summary:使用工具类实现通用分页处理 author: evan_zhao email: [email protected]
目前比较广泛使用的分页方式是将查询结果缓存在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) throws Exception{ 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 [email protected]字样
/////////////////////////////////// // // Page.java // author: [email protected] // ///////////////////////////////////
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 [email protected] <br> * @version 1.0 */ public class Page implements java.io.Serializable { public static final Page EMPTY_PAGE = new Page(); public static final int DEFAULT_PAGE_SIZE = 20; public static final int MAX_PAGE_SIZE = 9999;
private int myPageSize = DEFAULT_PAGE_SIZE;
private int start; private int avaCount,totalSize; private Object data;
private int currentPageno; private int totalPageCount;
/** * 默认构造方法,只构造空页 */ protected Page(){ this.init(0,0,0,DEFAULT_PAGE_SIZE,new Object()); }
/** * 分页数据初始方法,由子类调用 * @param start 本页数据在数据库中的起始位置 * @param avaCount 本页包含的数据条数 * @param totalSize 数据库中总记录条数 * @param pageSize 本页容量 * @param data 本页包含的数据 */ protected void 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(){ return this.data; }
/** * 取本页数据容量(本页能包含的记录数) * @return 本页能包含的记录数 */ public int getPageSize(){ return this.myPageSize; }
/** * 是否有下一页 * @return 是否有下一页 */ public boolean hasNextPage() { /* if (avaCount==0 && totalSize==0){ return false; } return (start + avaCount -1) < totalSize; */ return (this.getCurrentPageNo()<this.getTotalPageCount()); }
/** * 是否有上一页 * @return 是否有上一页 */ public boolean hasPreviousPage() { /* return start > 1; */ return (this.getCurrentPageNo()>1); }
/** * 获取当前页第一条数据在数据库中的位置 * @return */ public int getStart(){ return start; }
/** * 获取当前页最后一条数据在数据库中的位置 * @return */ public int getEnd(){ int end = this.getStart() + this.getSize() -1; if (end<0) { end = 0; } return end; }
/** * 获取上一页第一条数据在数据库中的位置 * @return 记录对应的rownum */ public int getStartOfPreviousPage() { return Math.max(start-myPageSize, 1); }
/** * 获取下一页第一条数据在数据库中的位置 * @return 记录对应的rownum */ public int getStartOfNextPage() { return start + avaCount; }
/** * 获取任一页第一条数据在数据库中的位置,每页条数使用默认值 * @param pageNo 页号 * @return 记录对应的rownum */ public static int getStartOfAnyPage(int pageNo){ return getStartOfAnyPage(pageNo, DEFAULT_PAGE_SIZE); }
/** * 获取任一页第一条数据在数据库中的位置 * @param pageNo 页号 * @param pageSize 每页包含的记录数 * @return 记录对应的rownum */ public static int getStartOfAnyPage(int pageNo, int pageSize){ int startIndex = (pageNo-1) * pageSize + 1; if ( startIndex < 1) startIndex = 1; //System.out.println("Page No to Start Index: " + pageNo + "-" + startIndex); return startIndex; }
/** * 取本页包含的记录数 * @return 本页包含的记录数 */ public int getSize() { return avaCount; }
/** * 取数据库中包含的总记录数 * @return 数据库中包含的总记录数 */ public int getTotalSize() { return this.totalSize; }
/** * 取当前页码 * @return 当前页码 */ public int getCurrentPageNo(){ return this.currentPageno; }
/** * 取总页码 * @return 总页码 */ public int getTotalPageCount(){ return this.totalPageCount; }
/** * * @param queryJSFunctionName 实现分页的JS脚本名字,页码变动时会自动回调该方法 * @param pageNoParamName 页码参数名称 * @return */ public String getHTML(String queryJSFunctionName, String pageNoParamName){ if (getTotalPageCount()<1){ return "<input type='hidden' name='"+pageNoParamName+"' value='1' >"; } if (queryJSFunctionName == null || queryJSFunctionName.trim().length()<1) { queryJSFunctionName = "gotoPage"; } if (pageNoParamName == null || pageNoParamName.trim().length()<1){ pageNoParamName = "pageno"; }
String gotoPage = "_"+queryJSFunctionName;
StringBuffer html = new StringBuffer("\n"); html.append("<script language=\"Javascript1.2\">\n") .append("function ").append(gotoPage).append("(pageNo){ \n") .append( " var curPage=1; \n") .append( " try{ curPage = document.all[\"") .append(pageNoParamName).append("\"].value; \n") .append( " document.all[\"").append(pageNoParamName) .append("\"].value = pageNo; \n") .append( " ").append(queryJSFunctionName).append("(pageNo); \n") .append( " return true; \n") .append( " }catch(e){ \n") // .append( " try{ \n") // .append( " document.forms[0].submit(); \n") // .append( " }catch(e){ \n") .append( " alert('尚未定义查询方法:function ") .append(queryJSFunctionName).append("()'); \n") .append( " document.all[\"").append(pageNoParamName) .append("\"].value = curPage; \n") .append( " return false; \n") // .append( " } \n") .append( " } \n") .append( "}") .append( "</script> \n") .append( ""); html.append( "<table border=0 cellspacing=0 cellpadding=0 align=center width=80%> \n") .append( " <tr> \n") .append( " <td align=left><br> \n"); html.append( " 共" ).append( getTotalPageCount() ).append( "页") .append( " [") .append(getStart()).append("..").append(getEnd()) .append("/").append(this.getTotalSize()).append("] \n") .append( " </td> \n") .append( " <td align=right> \n"); if (hasPreviousPage()){ html.append( "[<a href='javascript:").append(gotoPage) .append("(") .append(getCurrentPageNo()-1) .append( ")'>上一页</a>] \n"); } html.append( " 第") .append( " <select name='") .append(pageNoParamName).append("' onChange='javascript:") .append(gotoPage).append("(this.value)'>\n"); String selected = "selected"; for(int i=1;i<=getTotalPageCount();i++){ if( i == getCurrentPageNo() ) selected = "selected"; else selected = ""; html.append( " <option value='").append(i).append("' ") .append(selected).append(">").append(i).append("</option> \n"); } if (getCurrentPageNo()>getTotalPageCount()){ html.append( " <option value='").append(getCurrentPageNo()) .append("' selected>").append(getCurrentPageNo()) .append("</option> \n"); } html.append( " </select>页 \n"); if (hasNextPage()){ html.append( " [<a href='javascript:").append(gotoPage) .append("(").append((getCurrentPageNo()+1)) .append( ")'>下一页</a>] \n"); } html.append( "</td></tr></table> \n");
return html.toString();
} }
/////////////////////////////////// // // RowSetPage.java // author: [email protected] // /////////////////////////////////// package page;
import javax.sql.RowSet;
/** * <p>Title: RowSetPage</p> * <p>Description: 使用RowSet封装数据的分页对象</p> * <p>Copyright: Copyright (c) 2003</p> * @author [email protected] * @version 1.0 */
public class RowSetPage extends Page { private javax.sql.RowSet rs;
/** *空页 */ public static final RowSetPage EMPTY_PAGE = new RowSetPage();
/** *默认构造方法,创建空页 */ public RowSetPage(){ this(null, 0,0); }
/** *构造分页对象 *@param crs 包含一页数据的OracleCachedRowSet *@param start 该页数据在数据库中的起始位置 *@param totalSize 数据库中包含的记录总数 */ public RowSetPage(RowSet crs, int start, int totalSize) { this(crs,start,totalSize,Page.DEFAULT_PAGE_SIZE); }
/** *构造分页对象 *@param crs 包含一页数据的OracleCachedRowSet *@param start 该页数据在数据库中的起始位置 *@param totalSize 数据库中包含的记录总数 *@pageSize 本页能容纳的记录数 */ public RowSetPage(RowSet crs, int start, int totalSize, int pageSize) { try{ int avaCount=0; if (crs!=null) { crs.beforeFirst(); if (crs.next()){ crs.last(); avaCount = crs.getRow(); } crs.beforeFirst(); } rs = crs; super.init(start,avaCount,totalSize,pageSize,rs); }catch(java.sql.SQLException sqle){ throw new RuntimeException(sqle.toString()); } }
/** *取分页对象中的记录数据 */ public javax.sql.RowSet getRowSet(){ return rs; }
}
/////////////////////////////////// // // PagedStatement.java // author: [email protected] // ///////////////////////////////////
package page;
import foo.DBUtil;
import java.math.BigDecimal; import java.util.List; import java.util.Iterator; import java.util.Collections;
import java.sql.Connection; import java.sql.SQLException; import java.sql.ResultSet; import java.sql.Statement; import java.sql.PreparedStatement; import java.sql.Timestamp; import javax.sql.RowSet;
/** * <p>Title: 分页查询</p> * <p>Description: 根据查询语句和页码查询出当页数据</p> * <p>Copyright: Copyright (c) 2002</p> * @author [email protected] * @version 1.0 */ public abstract class PagedStatement { public final static int MAX_PAGE_SIZE = Page.MAX_PAGE_SIZE;
protected String countSQL, querySQL; protected int pageNo,pageSize,startIndex,totalCount; protected javax.sql.RowSet rowSet; protected RowSetPage rowSetPage;
private List boundParams;
/** * 构造一查询出所有数据的PageStatement * @param sql query sql */ public PagedStatement(String sql){ this(sql,1,MAX_PAGE_SIZE); }
/** * 构造一查询出当页数据的PageStatement * @param sql query sql * @param pageNo 页码 */ public PagedStatement(String sql, int pageNo){ this(sql, pageNo, Page.DEFAULT_PAGE_SIZE); }
/** * 构造一查询出当页数据的PageStatement,并指定每页显示记录条数 * @param sql query sql * @param pageNo 页码 * @param pageSize 每页容量 */ public PagedStatement(String sql, int pageNo, int pageSize){ this.pageNo = pageNo; this.pageSize = pageSize; this.startIndex = Page.getStartOfAnyPage(pageNo, pageSize); this.boundParams = Collections.synchronizedList(new java.util.LinkedList());
this.countSQL = "select count(*) from ( " + sql +") "; this.querySQL = intiQuerySQL(sql, this.startIndex, pageSize); }
/** *生成查询一页数据的sql语句 *@param sql 原查询语句 *@startIndex 开始记录位置 *@size 需要获取的记录数 */ protected abstract String intiQuerySQL(String sql, int startIndex, int size);
/** *使用给出的对象设置指定参数的值 *@param index 第一个参数为1,第二个为2,。。。 *@param obj 包含参数值的对象 */ public void setObject(int index, Object obj) throws SQLException{ BoundParam bp = new BoundParam(index, obj); boundParams.remove(bp); boundParams.add( bp); }
/** *使用给出的对象设置指定参数的值 *@param index 第一个参数为1,第二个为2,。。。 *@param obj 包含参数值的对象 *@param targetSqlType 参数的数据库类型 */ public void setObject(int index, Object obj, int targetSqlType) throws SQLException{ BoundParam bp = new BoundParam(index, obj, targetSqlType); boundParams.remove(bp); boundParams.add(bp ); }
/** *使用给出的对象设置指定参数的值 *@param index 第一个参数为1,第二个为2,。。。 *@param obj 包含参数值的对象 *@param targetSqlType 参数的数据库类型(常量定义在java.sql.Types中) *@param scale 精度,小数点后的位数 * (只对targetSqlType是Types.NUMBER或Types.DECIMAL有效,其它类型则忽略) */ public void setObject(int index, Object obj, int targetSqlType, int scale) throws SQLException{ BoundParam bp = new BoundParam(index, obj, targetSqlType, scale) ; boundParams.remove(bp); boundParams.add(bp); }
/** *使用给出的字符串设置指定参数的值 *@param index 第一个参数为1,第二个为2,。。。 *@param str 包含参数值的字符串 */ public void setString(int index, String str)throws SQLException{ BoundParam bp = new BoundParam(index, str) ; boundParams.remove(bp); boundParams.add(bp); }
/** *使用给出的字符串设置指定参数的值 *@param index 第一个参数为1,第二个为2,。。。 *@param timestamp 包含参数值的时间戳 */ public void setTimestamp(int index, Timestamp timestamp)throws SQLException{ BoundParam bp = new BoundParam(index, timestamp) ; boundParams.remove(bp); boundParams.add( bp ); }
/** *使用给出的整数设置指定参数的值 *@param index 第一个参数为1,第二个为2,。。。 *@param value 包含参数值的整数 */ public void setInt(int index, int value)throws SQLException{ BoundParam bp = new BoundParam(index, new Integer(value)) ; boundParams.remove(bp); boundParams.add( bp ); }
/** *使用给出的长整数设置指定参数的值 *@param index 第一个参数为1,第二个为2,。。。 *@param value 包含参数值的长整数 */ public void setLong(int index, long value)throws SQLException{ BoundParam bp = new BoundParam(index, new Long(value)) ; boundParams.remove(bp); boundParams.add( bp ); }
/** *使用给出的双精度浮点数设置指定参数的值 *@param index 第一个参数为1,第二个为2,。。。 *@param value 包含参数值的双精度浮点数 */ public void setDouble(int index, double value)throws SQLException{ BoundParam bp = new BoundParam(index, new Double(value)) ; boundParams.remove(bp); boundParams.add( bp); }
/** *使用给出的BigDecimal设置指定参数的值 *@param index 第一个参数为1,第二个为2,。。。 *@param bd 包含参数值的BigDecimal */ public void setBigDecimal(int index, BigDecimal bd)throws SQLException{ BoundParam bp = new BoundParam(index, bd ) ; boundParams.remove(bp); boundParams.add( bp); }
private void setParams(PreparedStatement pst) throws SQLException{ if (pst==null || this.boundParams==null || this.boundParams.size()==0 ) return ; BoundParam param; for (Iterator itr = this.boundParams.iterator();itr.hasNext();){ param = (BoundParam) itr.next(); if (param==null) continue; if (param.sqlType == java.sql.Types.OTHER){ pst.setObject(param.index, param.value); }else{ pst.setObject(param.index, param.value, param.sqlType, param.scale); } } }
/** * 执行查询取得一页数据,执行结束后关闭数据库连接 * @return RowSetPage * @throws SQLException */ public RowSetPage executeQuery() throws SQLException{ System.out.println("executeQueryUsingPreparedStatement"); Connection conn = DBUtil.getConnection(); PreparedStatement pst = null; ResultSet rs = null; try{ pst = conn.prepareStatement(this.countSQL); setParams(pst); rs =pst.executeQuery(); if (rs.next()){ totalCount = rs.getInt(1); } else { totalCount = 0; }
rs.close(); pst.close();
if (totalCount < 1 ) return RowSetPage.EMPTY_PAGE;
pst = conn.prepareStatement(this.querySQL); System.out.println(querySQL); pst.setFetchSize(this.pageSize); setParams(pst); rs =pst.executeQuery(); //rs.setFetchSize(pageSize);
this.rowSet = populate(rs);
rs.close(); rs = null; pst.close(); pst = null;
this.rowSetPage = new RowSetPage(this.rowSet,startIndex,totalCount,pageSize); return this.rowSetPage; }catch(SQLException sqle){ //System.out.println("executeQuery SQLException"); sqle.printStackTrace(); throw sqle; }catch(Exception e){ e.printStackTrace(); throw new RuntimeException(e.toString()); }finally{ //System.out.println("executeQuery finally"); DBUtil.close(rs, pst, conn); } }
/** *将ResultSet数据填充进CachedRowSet */ protected abstract RowSet populate(ResultSet rs) throws SQLException;
/** *取封装成RowSet查询结果 *@return RowSet */ public javax.sql.RowSet getRowSet(){ return this.rowSet; }
/** *取封装成RowSetPage的查询结果 *@return RowSetPage */ public RowSetPage getRowSetPage() { return this.rowSetPage; }
/** *关闭数据库连接 */ public void close(){ //因为数据库连接在查询结束或发生异常时即关闭,此处不做任何事情 //留待扩充。 }
private class BoundParam { int index; Object value; int sqlType; int scale;
public BoundParam(int index, Object value) { this(index, value, java.sql.Types.OTHER); }
public BoundParam(int index, Object value, int sqlType) { this(index, value, sqlType, 0); }
public BoundParam(int index, Object value, int sqlType, int scale) { this.index = index; this.value = value; this.sqlType = sqlType; this.scale = scale; }
public boolean equals(Object obj){ if (obj!=null && this.getClass().isInstance(obj)){ BoundParam bp = (BoundParam)obj; if (this.index==bp.index) return true; } return false; } }
}
/////////////////////////////////// // // PagedStatementOracleImpl.java // author: [email protected] // /////////////////////////////////// package page; import java.sql.ResultSet; import java.sql.SQLException; import javax.sql.RowSet; import oracle.jdbc.rowset.OracleCachedRowSet;
/** * <p>Title: 分页查询Oracle数据库实现</p> * <p>Copyright: Copyright (c) 2002</p> * @author [email protected] * @version 1.0 */ public class PagedStatementOracleImpl extends PagedStatement {
/** * 构造一查询出所有数据的PageStatement * @param sql query sql */ public PagedStatementOracleImpl(String sql){ super(sql); }
/** * 构造一查询出当页数据的PageStatement * @param sql query sql * @param pageNo 页码 */ public PagedStatementOracleImpl(String sql, int pageNo){ super(sql, pageNo); }
/** * 构造一查询出当页数据的PageStatement,并指定每页显示记录条数 * @param sql query sql * @param pageNo 页码 * @param pageSize 每页容量 */ public PagedStatementOracleImpl(String sql, int pageNo, int pageSize){ super(sql, pageNo, pageSize); }
/** *生成查询一页数据的sql语句 *@param sql 原查询语句 *@startIndex 开始记录位置 *@size 需要获取的记录数 */ protected String intiQuerySQL(String sql, int startIndex, int size){ StringBuffer querySQL = new StringBuffer(); if (size != super.MAX_PAGE_SIZE) { querySQL.append("select * from (select my_table.*,rownum as my_rownum from(") .append( sql) .append(") my_table where rownum<").append(startIndex + size) .append(") where my_rownum>=").append(startIndex); } else { querySQL.append("select * from (select my_table.*,rownum as my_rownum from(") .append(sql) .append(") my_table ") .append(") where my_rownum>=").append(startIndex); } return querySQL.toString(); }
/** *将ResultSet数据填充进CachedRowSet */ protected RowSet populate(ResultSet rs) throws SQLException{ OracleCachedRowSet ocrs = new OracleCachedRowSet(); ocrs.populate(rs); return ocrs; }
}
相关连接: JSP页面查询显示常用模式,介绍查询结果集封装的几种常用模式。本程序使用了其中部分代码 RowSet规范原来是JDBC(TM) 2.0 Optional Package的一部分,现在已经并入JDBC3.0规范,并且将成为J2SE1.5的组成部分。 关于RowSet的实现各个数据库的jdbc driver应该都有提供,oracle实现可以到http://otn.oracle.com/software/tech/java/sqlj_jdbc/content.html下载(Additional RowSet support) Sun也提供了RowSet的参考实现,应该可以支持大多数数据库:http://java.sun.com/products/jdbc/download.html PetStore 是Sun关于J2EE设计模式的一个示例程序。
|
|
评论人:sun2bin 参与分: 17739 专家分: 1020 来自: http://www.nju.edu.cn
|
发表时间: 2003-9-6 上午9:31 |
在我的印象里面,Oracle中如果使用where rownum > xxx的话将会一条记录也不返回。一般只使用rownum<xxx或rownum<=xxx。 |
|
评论人:sun2bin 参与分: 17739 专家分: 1020 来自: http://www.nju.edu.cn
|
发表时间: 2003-9-6 上午9:32 |
对不起,看错了。:P |
|
评论人:calmness 参与分: 3 专家分: 0 |
发表时间: 2003-9-8 下午10:12 |
若使用EJB的CMP来获得大的查询结果,该如何控制呢 |
|
评论人:evan 参与分: 429 专家分: 400 来自: Shanghai
|
发表时间: 2003-9-9 上午9:17 |
千万不要这么做,效率极其低下 |
|
评论人:mmgg 参与分: 45 专家分: 0 |
发表时间: 2003-9-11 上午10:19 |
这是我写的测试代码,为什么只能看第一页,不能翻到第二页! <%@ page import = "page.*"%> <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()"> </form> <%! int pageNo;%> <%
try{ //可以通过参数pageno获得用户选择的页码 pageNo = Integer.parseInt(request.getParameter("pageno") ); }catch(Exception ex){ //默认为第一页 pageNo=1; } String sql="select xh,xm,nsrsbh,nsrmc from fl_cjzgjcd"; //使用Oracle数据库的分页查询实现,每页显示5条
PagedStatement pst =new PagedStatementOracleImpl(sql, pageNo, 3); //pst.setString(1, gender); RowSetPage empPage = pst.executeQuery();
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("XH")%></td> <td><%= empRS.getString("XM")%></td> <td><%= empRS.getString("NSRSBH")%></td> <td><%= empRS.getString("NSRMC")%></td> </tr><% } // end while %> <tr> <% //显示总页数和当前页数(pageno)以及分页代码。 //此处doQuery为页面上提交查询动作的javascript函数名,pageno为标识当前页码的参数名 %> <td colspan=4><%= empPage.getHTML("doQuery", "pageno") %> </td> </tr> </table> |
|
评论人:mmgg 参与分: 45 专家分: 0 |
发表时间: 2003-9-11 上午11:08 |
OK了,你的代码中有个小问题!</form>应该是在</table>的后边,否则无法传递pageno参数给页面! |
|
评论人:evan 参与分: 429 专家分: 400 来自: Shanghai
|
发表时间: 2003-9-11 下午12:21 |
谢谢!
jsp代码中不要这么申明:<%! int pageNo;%>,因为这样pageNo是servlet成员变量,会存在多线程问题 |
|
评论人:mmgg 参与分: 45 专家分: 0 |
发表时间: 2003-9-12 下午1:28 |
明白了,evan,期待您的下一篇文章! |
|
评论人:mmgg 参与分: 45 专家分: 0 |
发表时间: 2003-9-12 下午4:44 |
evan,每次查询都要连接一次数据库是否影响效率,我看到的其他的代码大都是连接数据库后将连接放到session中,下次用的时候取出,不知道你是怎么考虑的? |
|
评论人:evan 参与分: 429 专家分: 400 来自: Shanghai
|
发表时间: 2003-9-16 下午9:29 |
如果连接保存在session中什么时候关闭?如果用户很多也会有问题吧? 一般解决方法是使用连接池,大多应用服务器或web framework都支持,自己写一个也不难
|
|
评论人:freedomlinux 参与分: 18 专家分: 0 |
发表时间: 2003-10-10 上午10:16 |
there are a little problem in JavaScript of "Page.java" .the writer use "document.all" ,but not "document.getElementById".so another browser (i.e mozilla) can not browse it.i correct the javaScript in it. so the linux user can browse it smoothly,here is the code. /////////////////////////////////////////////////////////////// public String getHTML(String queryJSFunctionName, String pageNoParamName){ if (getTotalPageCount()<1){ return "<input type='hidden' name='"+pageNoParamName+"' value='1' >"; } if (queryJSFunctionName == null || queryJSFunctionName.trim().length()<1) { queryJSFunctionName = "gotoPage"; } if (pageNoParamName == null || pageNoParamName.trim().length()<1){ pageNoParamName = "pageno"; }
String gotoPage = "_"+queryJSFunctionName; StringBuffer html = new StringBuffer("\n"); html.append("<script language=\"Javascript1.2\">\n") .append("function ").append(gotoPage).append("(pageNo){ \n") .append( " var curPage=1; \n") .append( " try{ curPage = document.getElementById(\"") .append("select_id").append("\").value; \n") .append( " document.getElementById(\"").append("select_id") .append("\").value = pageNo; \n") .append( " ").append(queryJSFunctionName).append("(pageNo); \n") .append( " return true; \n") .append( " }catch(e){ \n") // .append( " try{ \n") // .append( " document.forms[0].submit(); \n") // .append( " }catch(e){ \n") .append( " alert('Not define inquiry method :function ") .append(queryJSFunctionName).append("()'); \n") .append( " document.getElementById(\"").append("select_id") .append("\").value = curPage; \n") .append( " return false; \n") // .append( " } \n") .append( " } \n") .append( "}") .append( "</script> \n") .append( ""); html.append( "<table border=0 cellspacing=0 cellpadding=0 align=center width=80%> \n") .append( " <tr> \n") .append( " <td align=left><br> \n"); html.append( " 共" ).append( getTotalPageCount() ).append("页") .append( " [") .append(getStart()).append("..").append(getEnd()) .append("/").append(this.getTotalSize()).append("] \n") .append( " </td> \n") .append( " <td align=right> \n"); if (hasPreviousPage()){ html.append( "[<a href='javascript:").append(gotoPage) .append("(") .append(getCurrentPageNo()-1) .append( ")'>上一页</a>] \n"); } html.append( " 第") .append( " <select name='") .append(pageNoParamName).append("' id='select_id'").append(" onChange='javascript:") .append(gotoPage).append("(this.value)'>\n"); String selected = "selected"; for(int i=1;i<=getTotalPageCount();i++){ if( i == getCurrentPageNo() ) selected = "selected"; else selected = ""; html.append( " <option value='").append(i).append("' ") .append(selected).append(">").append(i).append("</option> \n"); } if (getCurrentPageNo()>getTotalPageCount()){ html.append( " <option value='").append(getCurrentPageNo()) .append("' selected>").append(getCurrentPageNo()) .append("</option> \n"); } html.append( " </select>页 \n"); if (hasNextPage()){ html.append( " [<a href='javascript:").append(gotoPage) .append("(").append((getCurrentPageNo()+1)) .append( ")'>下一页</a>] \n"); } html.append( "</td></tr></table> \n");
return html.toString();
} //////////////////////////////////////////////////////////////////////// Don't use document.all
I've lost count of the number of Javascript scripts I've seen floating around that include the equivalent of the following code snippet:
if (document.all) { element = document.all[id]; else { element = document.getElementById(id); }
document.all was introduced in Internet Explorer 4, because the W3C DOM hadn't yet standardised a way of grabbing references to elements using their ID. By the time IE 5 came out, document.getElementById() had been standardised and as a result, IE 5 included support for it.
IE 5 was released in September 1998. A popular browser statistics site (insert usual disclaimer as to the reliability of any stats but your own here) show IE 4's market share to be in the region of 1%. Even Netscape 4 has more users than that!
Don't use document.all. document.getElementById() is supported by every Javascript supporting browser released since 1998. |
|
客人: michaellhj |
发表时间: 2003-11-20 下午1:07 |
你好,记得你在JavaResearchOrganization上发过的帖子:”JSP分页技术实现“吗?
我看过后觉得很不错,决定采用你的方法了。首先注明的是我是新手,希望一些很低级的问题和错误请你原谅。
我是用websphere 5.0开发的,数据库是oracle;
我在做一个日志管理,所以在EJB中建立了OperatorLog,OperatorLogHome,OperatorLogBean,并且把你提供的那四个类,放在了searchPage中。而oracle的rowset的ocrs12.zip文件已经包含在oracle的jdbc中了。 另外,你的import foo.DBUtil我无法找到,于是建立一个DBUtil.java,实现import DBUtil,不知道这和foo.DBUtil有没有区别。好了,我想一起准备工作已经完成了。
当然,我现在做的是后台的调用,也就是在EJB进行检测我的开发。但是遇到了在PagedStatement.java中,执行rs =pst.executeQuery()时,出现了: [03-11-20 12:37:09:922 CST] 495b0a00 SystemOut O executeQueryUsingPreparedStatement [03-11-20 12:37:10:188 CST] 495b0a00 SystemErr R java.sql.SQLException: ORA-00907: 缺少右括号
[03-11-20 12:37:10:188 CST] 495b0a00 SystemErr R at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:134) [03-11-20 12:37:10:188 CST] 495b0a00 SystemErr R at oracle.jdbc.ttc7.TTIoer.processError(TTIoer.java:289) [03-11-20 12:37:10:188 CST] 495b0a00 SystemErr R at oracle.jdbc.ttc7.Oall7.receive(Oall7.java:573) [03-11-20 12:37:10:188 CST] 495b0a00 SystemErr R at oracle.jdbc.ttc7.TTC7Protocol.doOall7(TTC7Protocol.java:1891) [03-11-20 12:37:10:188 CST] 495b0a00 SystemErr R at oracle.jdbc.ttc7.TTC7Protocol.parseExecuteDescribe(TTC7Protocol.java:830) [03-11-20 12:37:10:188 CST] 495b0a00 SystemErr R at oracle.jdbc.driver.OracleStatement.doExecuteQuery(OracleStatement.java:2391) [03-11-20 12:37:10:188 CST] 495b0a00 SystemErr R at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:2672) [03-11-20 12:37:10:203 CST] 495b0a00 SystemErr R at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:589) [03-11-20 12:37:10:203 CST] 495b0a00 SystemErr R at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:527) [03-11-20 12:37:10:203 CST] 495b0a00 SystemErr R at com.ibm.ws.rsadapter.jdbc.WSJdbcPreparedStatement.executeQuery(WSJdbcPreparedStatement.java:498) [03-11-20 12:37:10:203 CST] 495b0a00 SystemErr R at searchPage.PagedStatement.executeQuery(PagedStatement.java:215) [03-11-20 12:37:10:203 CST] 495b0a00 SystemErr R at logmanager.ejb.OperatorLogBean.getOPERATOR_ID(OperatorLogBean.java:58) [03-11-20 12:37:10:219 CST] 495b0a00 SystemErr R at logmanager.ejb.EJSRemoteStatelessOperatorLog_67a0df88.getOPERATOR_ID(EJSRemoteStatelessOperatorLog_67a0df88.java:36) [03-11-20 12:37:10:219 CST] 495b0a00 SystemErr R at java.lang.reflect.Method.invoke(Native Method) [03-11-20 12:37:10:219 CST] 495b0a00 SystemErr R at com.ibm.rmi.corba.ServantObjectImpl$3.run(ServantObjectImpl.java:223) [03-11-20 12:37:10:219 CST] 495b0a00 SystemErr R at java.security.AccessController.doPrivileged(Native Method) [03-11-20 12:37:10:219 CST] 495b0a00 SystemErr R at com.ibm.rmi.corba.ServantObjectImpl.invoke(ServantObjectImpl.java:221) [03-11-20 12:37:10:219 CST] 495b0a00 SystemErr R at $Proxy1.getOPERATOR_ID(Unknown Source) [03-11-20 12:37:10:219 CST] 495b0a00 SystemErr R at logmanager.ejb._OperatorLog_Stub.getOPERATOR_ID(_OperatorLog_Stub.java:260) [03-11-20 12:37:10:219 CST] 495b0a00 SystemErr R at java.lang.reflect.Method.invoke(Native Method) [03-11-20 12:37:10:234 CST] 495b0a00 SystemErr R at com.ibm.etools.utc.model.ReflectionMethodModel.invoke(ReflectionMethodModel.java:68) [03-11-20 12:37:10:234 CST] 495b0a00 SystemErr R at com.ibm.etools.utc.servlet.InvokeServlet.invoke(InvokeServlet.java:110) [03-11-20 12:37:10:234 CST] 495b0a00 SystemErr R at com.ibm.etools.utc.servlet.InvokeServlet.doPost(InvokeServlet.java:323) [03-11-20 12:37:10:234 CST] 495b0a00 SystemErr R at javax.servlet.http.HttpServlet.service(HttpServlet.java:760) [03-11-20 12:37:10:234 CST] 495b0a00 SystemErr R at javax.servlet.http.HttpServlet.service(HttpServlet.java:853) [03-11-20 12:37:10:234 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.servlet.StrictServletInstance.doService(StrictServletInstance.java:110) [03-11-20 12:37:10:234 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.servlet.StrictLifecycleServlet._service(StrictLifecycleServlet.java:174) [03-11-20 12:37:10:234 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.servlet.IdleServletState.service(StrictLifecycleServlet.java:313) [03-11-20 12:37:10:234 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.servlet.StrictLifecycleServlet.service(StrictLifecycleServlet.java:116) [03-11-20 12:37:10:250 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.servlet.ServletInstance.service(ServletInstance.java:258) [03-11-20 12:37:10:250 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.servlet.ValidServletReferenceState.dispatch(ValidServletReferenceState.java:42) [03-11-20 12:37:10:250 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.servlet.ServletInstanceReference.dispatch(ServletInstanceReference.java:40) [03-11-20 12:37:10:250 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.handleWebAppDispatch(WebAppRequestDispatcher.java:872) [03-11-20 12:37:10:250 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.dispatch(WebAppRequestDispatcher.java:491) [03-11-20 12:37:10:250 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.forward(WebAppRequestDispatcher.java:173) [03-11-20 12:37:10:250 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.srt.WebAppInvoker.doForward(WebAppInvoker.java:79) [03-11-20 12:37:10:250 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.srt.WebAppInvoker.handleInvocationHook(WebAppInvoker.java:199) [03-11-20 12:37:10:250 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.cache.invocation.CachedInvocation.handleInvocation(CachedInvocation.java:71) [03-11-20 12:37:10:250 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.cache.invocation.CacheableInvocationContext.invoke(CacheableInvocationContext.java:114) [03-11-20 12:37:10:250 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.srp.ServletRequestProcessor.dispatchByURI(ServletRequestProcessor.java:187) [03-11-20 12:37:10:250 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.oselistener.OSEListenerDispatcher.service(OSEListener.java:331) [03-11-20 12:37:10:250 CST] 495b0a00 SystemErr R at com.ibm.ws.webcontainer.http.HttpConnection.handleRequest(HttpConnection.java:56) [03-11-20 12:37:10:266 CST] 495b0a00 SystemErr R at com.ibm.ws.http.HttpConnection.readAndHandleRequest(HttpConnection.java:432) [03-11-20 12:37:10:266 CST] 495b0a00 SystemErr R at com.ibm.ws.http.HttpConnection.run(HttpConnection.java:343) [03-11-20 12:37:10:266 CST] 495b0a00 SystemErr R at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:592)
我不知道怎么解决,希望你能够告诉我,万分感谢!
另附:一些改动已标明(粗体): EJB中的SESSIONBEAN-----------------OperatorLogBean.java package logmanager.ejb; import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.PreparedStatement;
import oracle.jdbc.rowset.OracleCachedRowSet;
import searchPage.*; /** * Bean implementation class for Enterprise Bean: OperatorLog */ public class OperatorLogBean implements javax.ejb.SessionBean { private javax.ejb.SessionContext mySessionCtx; /** * getSessionContext */ public javax.ejb.SessionContext getSessionContext() { return mySessionCtx; } /** * setSessionContext */ public void setSessionContext(javax.ejb.SessionContext ctx) { mySessionCtx = ctx; } /** * ejbCreate */ public void ejbCreate() throws javax.ejb.CreateException { } /** * ejbActivate */ public void ejbActivate() { } /** * ejbPassivate */ public void ejbPassivate() { } /** * ejbRemove */ public void ejbRemove() { } public RowSetPage getOPERATOR_ID(String OPERATOR_ID, int pageNo) throws Exception{ String sql="SELECT OPERATION_LOG.LOG_ID,OPERATION_LOG.LOG_TIME,OPERATION_LOG.LOG_CONTENT,OPERATOR.OPERATOR_ID,OPERATOR.OPERATOR_NAME,OPERATOR.PASSWORD,OPERATOR.OPERATOR_STATE,ORGANIZE.ORG_ID,ORGANIZE.ORG_NAME,ORGANIZE.ORG_TYPE,TERMINAL_INFO.IP,SYSTEM_MODULE.MODULE_ID,ROLE_INFO.ROLE_ID,ROLE_INFO.ROLE_NAME,ROLE_INFO.ROLE_TYPE,ROLE_INFO.ROLE_INHERIT,MODULE_FUNCTION.FUNCTION_ID,MODULE_FUNCTION.FUNCTION_NAME,MODULE_FUNCTION.FUNCTION_URL,SYSTEM_MODULE.MODULE_NAME,SYSTEM_MODULE.MODULE_DEPTH,SYSTEM_MODULE.ROOT_ID,SYSTEM_MODULE.PARENT_ID"; sql=sql+"FROM OPERATION_LOG,OPERATOR,ORGANIZE,TERMINAL_INFO,MODULE_FUNCTION,OPERATOR_ROLE,ROLE_INFO,ROLE_PERMISSION,SYSTEM_MODULE"; sql=sql+"WHERE ( OPERATOR.OPERATOR_ID = OPERATION_LOG.OPERATOR_ID ) and ( ORGANIZE.ORG_ID = OPERATOR.ORG_ID ) and ( TERMINAL_INFO.ORG_ID = ORGANIZE.ORG_ID ) and ( OPERATOR_ROLE.OPERATOR_ID = OPERATOR.OPERATOR_ID ) and ( ROLE_INFO.ROLE_ID = OPERATOR_ROLE.ROLE_ID ) and ( ROLE_INFO.ORG_ID = ORGANIZE.ORG_ID ) and ( ROLE_PERMISSION.ROLE_ID = ROLE_INFO.ROLE_ID ) and ( ROLE_PERMISSION.FUNCTION_ID = MODULE_FUNCTION.FUNCTION_ID ) and ( SYSTEM_MODULE.MODULE_ID = ROLE_PERMISSION.FUNCTION_MODULE ) and ( SYSTEM_MODULE.MODULE_ID = MODULE_FUNCTION.FUNCTION_MODULE ) and ( OPERATION_LOG.OPERATOR_ID = ? ) "; //使用Oracle数据库的分页查询实现,每页显示5条 PagedStatement pst =new PagedStatementOracleImpl(sql,pageNo, 5); pst.setString(1, OPERATOR_ID); return pst.executeQuery(); }
}
你编写的四个文件没有修改,只是对DBUtil.java中的jndi做了改变。
private static void initDataSource() throws Exception{ // Put connection properties in to a hashtable.
if (ctx == null) { ctx = new InitialContext(); } if (ds == null) { ds = (javax.sql.DataSource) ctx.lookup("java:comp/env/jdbc/ejbPool"); } } |
|
|