首页 >> 民生救助

解决MVC下分页显示的问题节能

民生救助  2020-10-02 07:57 字号: 大 中 小

前几天做一个系统,用到的是Tomcat+struts+Mysql的MVC框架。由于很多模块都需要分页,想写一个分页的方法。常见的方法每次翻页都查询一次数据库,从ResultSet中只取出一页数据(使用st();tRow()获得总计录条数,使用solute()定位到本页起始记录)。这种方式在某些数据库(如oracle)的JDBC实现中差不多也是需要遍历所有记录,实验证明在记录数很大时速度非常慢。  至于缓存结果集ResultSet的方法则完全是一种错误的做法。因为ResultSet在Statement或Connection关闭时也会被关闭,如果要使ResultSet有效势必长时间占用数据库连接。

因此比较好的分页做法应该是每次翻页的时候只从数据库里检索页面大小的块区的数据。这样虽然每次翻页都需要查询数据库,但查询出的记录数很少,络传输数据量不大,如果使用连接池更可以略过最耗时的建立数据库连接过程。而在数据库端有各种成熟的优化技术用于提高查询速度,比在应用服务器层做缓存有效多了。

所以我用到了从数据库中查询当前页面记录的办法。由于设计到公司核心代码的缘故,我就简单的用一个图书的例子介绍一下是我的思路:

开发思路:

我是用Struts的,当然就要使用到MVC这个模式,分页的时候也是这样的。

首先要有一个和数据库链接的bean,我们暂时叫DBUtil吧,这里面封装了很多和数据库有关的东西,比如有查询,修改,插入等方法,这是一个基本的类,这里我们用到的是查询,这个方法返回的数据类型是Object[][]。这里大家要注意一下,你也可以返回别的类型,但是一定要注意把后面对应的程序也修改一下。一下是这个类的部分代码:

package del;

import nstants;

import l.*;

import il.*;

/**

* Created by IntelliJ IDEA.

* User: 7612CE

* Date:

* Time: 21:41:38

* To change this template use Options | File Templates.

*/

public class DBUtil {

String sDBDriver=Driver;

String url=Url;

String dbUser=User;

String dbPassword=Password;

Connection conn=null;

PreparedStatement stmt=null;

ResultSet rs=null;

public DBUtil()throws ClassNotFoundException{

try{

rName(sDBDriver);

conn=tConnection(url,dbUser,dbPassword);

}catch(Exception e){

intln("DBUtil():"+tMessage());

}

}

/**

* Search some record in object table

* @param sql sql segment

* @ param map values for match

* @return Collection

*/

public Object[][] doSearch(String sql,Object [] data)throws SQLException{

PreparedStatement stmt = epareStatement(sql);

for(int i=0;data!=nulli int("the aql is ="+sql);

intln("data is " + data[i]);

tObject(i+1,data[i]);

}

ResultSet rset = ecuteQuery();

ResultSetMetaData rsm = tMetaData();

int col = tColumnCount();

ArrayList list = new ArrayList();

//Each element of list contains a record of resultset

while(xt()){

d(getLine(rset,col));

}

if(ze()==0||col==0){

closePrepStmt();

return null;

}

closePrepStmt();

//Construct box as a data matrix

Object[][] box = new Object[ze()][col];

for(int i=0;i for(int j=0;j {

box[i][j] =((Object[])t(i))[j];

}

return box;

}

[下一页]

由于是写分页的,当然也是要有一个page的类,具体代码如下,由于有很多注释,不用一一介绍了:

package il;

/**

* Title: 分页对象

* Description: 用于包含数据及分页信息的对象

*Page类实现了用于显示分页信息的基本方法,但未指定所含数据的类型,

*可根据需要实现以特定方式组织数据的子类,

*如RowSetPage以RowSet封装数据,ListPage以List封装数据

* Copyright: Copyright (c) 2002

* @author evan_zhao@

* @version 1.0

*/

public class Page implements rializable {

public static final Page EMPTY_PAGE = new Page();

public static final int DEFAULT_PAGE_SIZE = 2;

public static final int MAX_PAGE_SIZE = 2;

private int myPageSize = DEFAULT_PAGE_SIZE;

private int start;

private int avaCount,totalSize;

private Object[][] data=null;

private int currentPageno;

private int totalPageCount;

/**

* 默认构造方法,只构造空页

*/

public Page(){

it(0,0,0,DEFAULT_PAGE_SIZE,null);

}

/**

*构造分页对象

*@param crs 包含一页数据的OracleCachedRowSet

*@param start 该页数据在数据库中的起始位置

*@param totalSize 数据库中包含的记录总数

*@param pageSize 本页能容纳的记录数

*/

public Page(Object[][] crs, int start, int totalSize, int pageSize) {

try{

int avaCount=0;

if (crs!=null) {

avaCount = ngth;

}

data = crs;

it(start,avaCount,totalSize,pageSize,data);

}catch(Exception ex){

throw new RuntimeException(String());

}

}

/**

* 分页数据初始方法

* @param start 本页数据在数据库中的起始位置

* @param avaCount 本页包含的数据条数

* @param totalSize 数据库中总记录条数

* @param pageSize 本页容量

* @param data 本页包含的数据

*/

protected void init(int start, int avaCount, int totalSize, int pageSize, Object[][] data){

aCount =avaCount;

PageSize = pageSize;

art = start;

talSize = totalSize;

ta=data;

//intln("avaCount:"+avaCount);

//intln("totalSize:"+totalSize);

if (avaCounttotalSize) {

//throw new RuntimeException("记录条数大于总条数?!");

}

rrentPageno = (start -1)/pageSize +1;

talPageCount = (totalSize + pageSize -1) / pageSize;

if (totalSize==0 avaCount==0){

rrentPageno = 1;

talPageCount = 1;

}

//intln("Start Index to Page No: " + start + "-" + currentPageno);

}

public Object[][] getData(){

return ta;

}

/**

* 取本页数据容量(本页能包含的记录数)

* @return 本页能包含的记录数

*/

public int getPageSize(){

return PageSize;

}

/**

* 是否有下一页

* @return 是否有下一页

*/

public boolean hasNextPage() {

/*

if (avaCount==0 totalSize==0){

return false;

}

return (start + avaCount -1) totalSize;

*/

return (tCurrentPageNo() }

/**

* 是否有上一页

* @return 是否有上一页

*/

public boolean hasPreviousPage() {

/*

return start 1;

*/

return (tCurrentPageNo()1);

}

/**

* 获取当前页第一条数据在数据库中的位置

* @return

*/

public int getStart(){

return start;

}

/**

* 获取当前页最后一条数据在数据库中的位置

* @return

*/

public int getEnd(){

int end = tStart() + tSize() -1;

if (end0) {

end = 0;

}

return end;

}

/**

* 获取上一页第一条数据在数据库中的位置

* @return 记录对应的rownum

*/

public int getStartOfPreviousPage() {

return x(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;

//intln("Page No to Start Index: " + pageNo + "-" + startIndex);

return startIndex;

}

/**

* 取本页包含的记录数

* @return 本页包含的记录数

*/

public int getSize() {

return avaCount;

}

/**

* 取数据库中包含的总记录数

* @return 数据库中包含的总记录数

*/

public int getTotalSize() {

return talSize;

}

/**

* 取当前页码

* @return 当前页码

*/

public int getCurrentPageNo(){

return rrentPageno;

}

/**

* 取总页码

* @return 总页码

*/

public int getTotalPageCount(){

return talPageCount;

}

}

[下一页]

由于是用mvc这样的框架,所以在c这一层仅仅起到一个转向和收集页面信息的作用,所以这里我有写了一个分页查询的bean,暂时命名是PageCtBean,代码如下:

package il;

import Util;

import l.*;

/**

* Created by IntelliJ IDEA.

* User: 7612ce

* Date:

* Time: 10:36:57

* To change this template use Options | File Templates.

*/

public class PageCtBean {

public final static int MAX_PAGE_SIZE = X_PAGE_SIZE;

protected String countSQL, querySQL;

protected int pageNo,pageSize,startIndex,totalCount;

protected wSet rowSet;

protected Page setPage;

protected Object[][] objTables=null;

protected Object[][] objCount=null;

protected Object[] obj=null;

public PageCtBean(){

}

/**

* 构造一查询出所有数据的PageStatement

* @param sql query sql

*/

public PageCtBean(String sql){

this(sql,1,MAX_PAGE_SIZE);

}

/**

* 构造一查询出当页数据的PageStatement

* @param sql query sql

* @param pageNo 页码

*/

public PageCtBean(String sql, int pageNo){

this(sql, pageNo, FAULT_PAGE_SIZE);

}

/**

* 构造一查询出当页数据的PageStatement,并指定每页显示记录条数

* @param sql query sql

* @param pageNo 页码

* @param pageSize 每页容量

*/

public PageCtBean(String sql, int pageNo, int pageSize){

geNo = pageNo;

geSize = pageSize;

artIndex = tStartOfAnyPage(pageNo, pageSize);

erySQL = intiQuerySQL(sql, artIndex, pageSize);

}

/**

*生成查询一页数据的sql语句

*@param sql 原查询语句

*@ startIndex 开始记录位置

*@ size 需要获取的记录数

*/

protected String intiQuerySQL(String sql, int startIndex, int size){

StringBuffer querySQL = new StringBuffer();

pend(sql)

.append(" limit ")

.append(startIndex-1 )

.append(",").append(size);

return String();

}

/**

*使用给出的对象设置指定参数的值

*@param obj 包含参数值的对象

*/

public void setObject(Object obj[]) throws SQLException{

j=obj;

}

public void setCountSql(String sql){

untSQL=sql;

}

/**

* 执行查询取得一页数据,执行结束后关闭数据库连接

* @return RowSetPage

* @throws SQLException

*/

public Page executeQuery() throws ClassNotFoundException{

intln("executeQueryUsingPreparedStatement");

DBUtil DBean=new DBUtil();

try{

objCount = Search(untSQL,obj);

if (!uals(null)){

intln("the count is ="+objCount[0][0].toString());

totalCount =rseInt(objCount[0][0].toString()) ;

intln("the count is ="+totalCount);

} else {

totalCount = 0;

}

if (totalCount 1 )

return null;

objTables= Search(erySQL,obj);

tPage = new Page(jTables,startIndex,totalCount,pageSize);

return tPage;

}catch(SQLException sqle){

//intln("executeQuery SQLException");

intStackTrace();

}catch(Exception e){

intStackTrace();

throw new RuntimeException(String());

}

return null;

}

/**

*取封装成Page的查询结果

*@return Page

*/

public Page getPage() {

return tPage;

}

}

[下一页]

接下来是Action里面的代码,暂时定义这个Action 是ComputerAction,代码如下:

package tion;

import tion;

import tionForward;

import tionMapping;

import tionForm;

import g;

import gFactory;

import tpServletRequest;

import tpServletResponse;

import tpSession;

import ginForm;

import ginBean;

import puterBean;

import seView;

import ge;

import nctionManager;

import nstants;

import LBook;

import sultSet;

/**

* Created by IntelliJ IDEA.

* User: 7612CE

* Date:

* Time: 13:31:34

* To change this template use Options | File Templates.

*/

public class ComputerAction extends BaseAction {

private Log log=tLog(tClass().getName());

public ActionForward execute(ActionMapping mapping,

ActionForm Form,

HttpServletRequest request,

HttpServletResponse response){

boolean flag=false;

Object[][] obj=null;

Page page=new Page();

Integer id=new Integer();

String sql=puter_select_SQL;

BaseView view=new BaseView();

String pageNo = tParameter("pageNo");

if (pageNo == null || uals("null") || ngth() = 0) {

pageNo = "1";

}

try{

Object[] table={id};

ComputerBean computerBean=new ComputerBean();

tBeanDate(sql,table);

tPageNo(pageNo);

page=tResult();

obj=tData();

if(!uals(null)){

flag=true;

tObject(obj);

tAttribute(ERY_RESULT,view);

tAttribute("page",page);

}

}catch(Exception ex){

intStackTrace();

}

("system print the flag ="+flag);

if(flag){

return(ndForward(RWARD_SUCCESS));

}else{

return(ndForward(RWARD_FAILURE));

}

}

}

[下一页]

由于Action里面用到了查询的SQL语句,所有SQL语句写在一个特定的类中,这个类名定义为SQLBook,代码如下:

public class SQLBook {

public SQLBook(){}

/**

* computer sql

*/

public static final String Computer_select_SQL=

"select ,okname,okclass,assname,"+

"thor,blish,okno,ntent,ince,ount,"+

"av_number,gtime,cture from book a,bookclass b"+

" where okclass = and okclass=? "+

" order by desc ";

public static final String Computer_select_count_sql=

"select count(*) from book a,bookclass b"+

" where okclass = and okclass=? "+

" order by desc ";

}

到此为止,基本上分页的代码基本完成,为了使得分页的代码共用,我把他封装成了一个标签,这个自定义的标签命名为PaginatorTag,代码如下:

package il;

import Exception;

import pException;

import dyTagSupport;

import g;

import gFactory;

public class PaginatorTag extends BodyTagSupport {

protected Log log = tLog(tClass());

//以下是一标签中的一些属性,后面有较详细的介绍

int currentPage = 1;//当前页码

String url = "";//转向的地址

int totalSize = 0;//总的记录数

int perPage = 20;//每页显示的记录数目

boolean showTotal = true;//是否显示总数量

boolean showAllPages = false;//是否显示总页码

String strUnit ="";//计数单位

//得到当前页码

public int getCurrentPage() {

return currentPage;

}

//设置当前页码

public void setCurrentPage(int currentPage) {

rrentPage = currentPage;

}

//得到每页显示记录的数目

public int getMaxPerPage() {

return perPage;

}

//设置每页显示的记录数目

public void setMaxPerPage(int perPage) {

rPage = perPage;

}

//判断是否显示总的页码数目

public boolean isShowAllPages() {

return showAllPages;

}

//设置是否显示总的页码数目

public void setShowAllPages(boolean showAllPages) {

owAllPages = showAllPages;

}

//判断是否显示总的记录数目

public boolean isShowTotal() {

return showTotal;

}

//设置是否显示总的记录数目

public void setShowTotal(boolean showTotal) {

owTotal = showTotal;

}

//得到计数单位

public String getStrUnit() {

return strUnit;

}

//设置计数单位

public void setStrUnit(String strUnit) {

rUnit = strUnit;

}

//得到总的记录数目

public int getTotalPut() {

return totalSize;

}

//设置总的记录数目

public void setTotalPut(int totalSize) {

talSize = totalSize;

}

//得到转向的链接地址

public String getUrl() {

return url;

}

//设置链接地址

public void setUrl(String url) {

l = url;

}

public int doStartTag() throws JspException {

return SKIP_BODY;

}

public int doEndTag() throws JspException {

String out = showPage(currentPage, url, totalSize, perPage, showTotal, showAllPages, strUnit);

try {

tOut().print(out);

} catch (IOException e) {

intStackTrace();

}

return EVAL_PAGE;

}

/**

* 作 用:显示“上一页 下一页”等信息

*

* @param url

* ----链接地址

* @ param totalSize

* ----总数量

* @ param perPage

* ----每页数量

* @param showTotal

* ----是否显示总数量

* @param showAllPages

* ---是否用下拉列表显示所有页面以供跳转。有某些页面不能使用,否则会出现JS错误。

* @param strUnit

* ----计数单位

* @return .

* @ throws IOException

*/

protected String showPage(int currentPage,String url, int totalSize, int perPage,

boolean showTotal, boolean showAllPages, String strUnit){

int n = 0;

StringBuffer buf = new StringBuffer();

String strUrl;

n = (totalSize + perPage -1) / perPage;

pend("table align=''center''trtd");

if (showTotal == true)

pend("共 b" + totalSize + "/b " + strUnit

+ " ");

strUrl = JoinChar(url);

if (currentPage 2) {

pend("首页 上一页 ");

} else {

pend("a href=''" + strUrl + "pageNo=1'' title=''首页''首页/a

");

pend("a href=''" + strUrl + "pageNo=" + (currentPage

- 1)

+ "'' title=''上一页''上一页/a ");

}

if (n - currentPage 1)

pend("下一页 尾页");

else {

pend("a href=''" + strUrl + "pageNo=" + (currentPage

+ 1)

+ "'' title=''下一页''下一页/a ");

pend("a href=''" + strUrl + "pageNo=" + n + "''

title=''尾页''尾页/a");

}

pend(" 页次:strongfont color=red" + currentPage

+ "/font/" + n + "/strong页 ");

pend(" b" + perPage + "/b" + strUnit

+ "/页");

if (showAllPages == true) {

buf

.append(" 转到:select name=''page'' size=''1'' onchange=\"javascript:cation=''"

+ strUrl

+ "pageNo="

+ "''+tions[lectedIndex].value;\"");

for (int i = 1; i = n; i++) {

pend("option value=''" + i + "''");

if(currentPage == i)

pend(" selected ");

pend("第" + i + "页/option");

}

pend("/select");

}

pend("/td/tr/table");

return (String());

}

/**

* 向地址中加入 ? 或

* @param strUrl

* ----址.

* @return 加了 ? 或 的址.

*/

protected String JoinChar(String strUrl) {

String result = "";

if (uals("") || ngth() = 0) {

return result;

}

if (dexOf("?") ngth()) {

if (dexOf("?") -1) {

if (dexOf("") ngth()) {

result = strUrl + "";

} else {

result = strUrl;

}

} else {

result = strUrl + "?";

}

} else {

result = strUrl;

}

return result;

}

}

[下一页]

有了自定义标签,当然少不了用于处理标签的tld,我们定义一个d,代码如下:

?xml version="1.0" encoding="UTF-8"?

!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library

1.1//EN" ""

taglib

tlibversion1.0/tlibversion

jspversion1.1/jspversion

shortnameout/shortname

uri

infoTab Library for PaginatorTag/info

tag

namepaginator/name

ginatorTag/tagclass

bodycontentJSP/bodycontent

infoReturns a paginator/info

attribute

namecurrentPage/name

requiredtrue/required

rtexprvaluetrue/rtexprvalue

/attribute

attribute

nameurl/name

requiredtrue/required

rtexprvaluetrue/rtexprvalue

/attribute

attribute

nametotalPut/name

requiredtrue/required

rtexprvaluetrue/rtexprvalue

/attribute

attribute

namemaxPerPage/name

requiredtrue/required

rtexprvaluetrue/rtexprvalue

/attribute

attribute

nameshowTotal/name

requiredtrue/required

rtexprvaluefalse/rtexprvalue

/attribute

attribute

nameshowAllPages/name

requiredtrue/required

rtexprvaluefalse/rtexprvalue

/attribute

attribute

namestrUnit/name

requiredtrue/required

rtexprvaluefalse/rtexprvalue

/attribute

/tag

/taglib

[下一页]

好了,到现在我们开始来真正看一下jsp中的处理,我们写一个p,代码如下:

%@ page import="seView,

nstants,

ge"%

%@ page contentType="text/html;charset=GB2312" language="java"

errorPage=""%

%@ taglib uri="/WEB-INF/d"prefix="swtag"%

html

head

title/title

/head

body bgcolor="#e3edfc"

div id="Layer1" style="position:absolute; left:1px; top:2px;

width:780px; height:406px; z-index:1; background-color: #e3edfc; layer-background-color:

#e3edfc; border: 1px none #000000;"

table width="100%" cellpadding="1"

tr

td colspan="6"

table width="100%" cellpadding="1"

tr

td width="20%" align="center"BOOKNAME/td

td width="10%" align="center"AUTHOR/td

td width="10%" align="center"TYPE/td

td width="30%" align="center"PUBLISH/td

td width="10%" align="center"PRINCE/td

td colspan="2" align="center"SELECT/td

/tr

%

String contextPath = tContextPath();

String url=contextPath+"/";

BaseView view=(BaseView)tAttribute(ERY_RESULT);

Page setpage=(Page)tAttribute("page");

int currentPage=tCurrentPageNo();

intln("this is currentPage="+currentPage);

int totalPut=tTotalSize();

intln("this is totalPut="+totalPut);

int maxPerPage=tPageSize();

intln("this is maxPerPage="+maxPerPage);

if(veRecord()){

String sBgcolor="";

int length=tRecordCount();

for(int i=0;ilength;i++){

String type =tValue(i,2);

if(uals("1")){

type="computer";

}

if(i%2!=0){

sBgcolor="#A5C6EB";

}

else{

sBgcolor="#B7D7EF";

}

%

tr bgcolor=%=sBgcolor% height="10"

td align="center" %=tValue(i,1)%/td

td align="center"%=tValue(i,4)%/td

td align="center"%=type%/td

td align="center"%=tValue(i,5)%/td

td align="center"%=tValue(i,8)%/td

td width="20%" align="center"BUY PARTICULAR/td

/tr

%}}%

/table

/td

/tr

tr

swtag:paginator url="%=url%"

currentPage="%=currentPage%"

totalPut="%=totalPut%"

maxPerPage="%=maxPerPage%"

showTotal="true"

showAllPages="true"

strUnit="页" /

/tr

/table

/div

/body

/html

[下一页]

到此为止,分页的类基本完成,这样的话可以在别的模块都可以用这个标签,同时在开发别的系统的时候这个标签也可以使用,具有比较好的可移植性。这个数据库是mysql的,要是oracle的,仅仅在PageCtBean类中的intiQuerySQL方法里面改成

protected String intiQuerySQL(String sql, int startIndex, int size){

StringBuffer querySQL = new StringBuffer();

if (size != X_PAGE_SIZE) {

pend("select * from (select my_table.*,rownum as my_rownum from(")

.append( sql)

.appen他肯定也要给我娃儿1000元d(") my_table where rownum").append(startIndex + size)

.append(") where my_rownum=").append(startIndex);

} else {

pend("select * from (select my_table.*,rownum as my_rownum from(")

.append(sql)

.append(") my_table ")

.append(") where my_rownum=").append(startIndex);

}

return String();

}

就可以了。

同时在数据库中,返回当前页需要显示的数据,主要有以下方法:

a.使用mysql控制:

select * from user

order by Host

limit m, n

结果返回的是第m+1行到第n行的数据集。比如:

select * from user

order by Host

limit 1, 5

返回的是第2行到第5行的数据集

b.使用sqlserver

SELECT *

FROM (SELECT TOP m *

FROM (SELECT TOP n *

FROM Customers) A

ORDER BY CustomerID DESC) B

ORDER BY CustomerID

获得的结果集数据为第n-m+1行到第n行。

对整个过程的解释:

首先按照升序得到前n行的结果集A,然后按照降序从A中得到后m行的结果集B,最后按照升序对B进行重新排序,返回结果集。其中CustomerID为主键,比如:

SELECT *

FROM (SELECT TOP 5 *

FROM (SELECT TOP 10 *

FROM Customers) A

ORDER BY CustomerID DESC) B

ORDER BY CustomerID

的意思就是返中国海运和南航均被点名“公款打高尔夫球”;中石化的部分下属企业“公款吃喝、旅游问题频发”回包含第6行到第10行的数据结果集。

c.使用Oracle:

select * from (select rownum r ,* from test) tt

where tt.r 50 and tt.r = 100;

宜春治白癜风哪家医院比较好
宝宝拉肚子可以吃什么水果
德阳白癜风较好医院
推荐资讯