分享
 
 
 

Jive中的分页处理

王朝java/jsp·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

在Web应用中,难免和数据库打交道,对于返回的结果集,究竟该如何处理?

尤其是,当你的一个查询可能会返回数万条纪录的时候,你还能像某些骗钱骗精力的jsp垃圾书本上介绍的用rs.next()来处理么?

呵呵,我以前就是这么干的,直接传回resultset,在jsp中while(rs.next())循环中一个

一个的处理。

还有,返回结果的分页处理的问题。很多时候我们的Web应用(或者其他)都是针对某一种数据库的(感觉商业应用绝大多数是oracle,业余的多是mysql,还敢用jdbc连接sqlServer的,估计是被M$养废了)。所以我常常会针对某些数据库的特性写一些专门的SQL语句或者存储过程来确定返回结果集的大小,以实现分页处理(其实偶是数据库白痴,除了select/insert/update,其他的不知道!)。我以前就是用伪列来实现在Oracle上处理记录结果集的分页的。

慢慢的,为了所谓的降低代码耦合度,我对查询结果集的代码进行了重构,于是稍微进步了一点,在某个queryManager之类的类中传出一个Enumeration,里边的每个对象对应了数据库中的一条纪录。嘿嘿,居然也能凑合着对付500多人使用了。

可是,这也不是最好的办法,毕竟,如果真的返回了几万条纪录,这样的处理方法不但占内存,而且效率也不搞。

记得以前在bqlr.com中看到一篇关于“查询结果返回rs,还是集合”的讨论,答案是用Iterator。

可是里边的代码很笼统,对于从未用过iterator模式的人来说,基本上没有什么可以模仿和参照的。

还好,我们有现成的,那就是Jive。

看看Jive中是怎么实现的。

(A)我们以forum.jsp这个页面为例,缺省的,每页显示15条thread,用户可以指定从第n条开始显示到n+page_size条。

代码如下:

ResultFilter filter = new ResultFilter();

filter.setStartIndex(start); //从第start条thread开始

filter.setNumResults(range); //页面大小

// Set the moderation level minimum

filter.setModerationRangeMin(forum.getModerationMinThreadValue());

// More forum properties

int numThreads = forum.getThreadCount(filter);

int numMessages = forum.getMessageCount(filter);

// Iterator of threads

ForumThreadIterator threads = forum.threads(filter); //返回thread Iterator,

通过分析上面代码,明确了需要重点分析ResultFilter,DBForum和ForumThreadIterator这三个类。

(B)看看ResultFilter.java

这个类其实就是专门保存查询条件的,例如,可以查询某天之前,由某人发布的贴子等。一次查询操作的所有查询条件都保存在这个类里边。

里边有一个函数比较有意思:public void setSortPropertyName(String sortPropertyName),嘿嘿可以把“是否具备某个property”作为查询条件。我本来就想给jive添加thread置顶功能,看起来只要给需要置顶的thread加一个topMost属性,然后改动skin就可以了。可谓是得来全不费工夫啊。

把查询条件填充完毕以后,就可以把resultFilter传给forum.threads()取结果了。

(C)看DBForum.threads()

public ForumThreadIterator threads(ResultFilter resultFilter) {

//根据传入的resultFilter构造一个查询SQL语句(*注1)

String query = getThreadListSQL(resultFilter, false);

//返回一个确定返回的id列表,这里缺省大小是400个,例如,我要看1~15条纪录

// threadBlock的length是400,从1~400(*注2)

long [] threadBlock = getBlock(query.toString(), resultFilter.getStartIndex());

int startIndex = resultFilter.getStartIndex();

int endIndex;

// If number of results is set to inifinite, set endIndex to the total

// number of threads in the forum.

if (resultFilter.getNumResults() == ResultFilter.NULL_INT) {

endIndex = (int)getThreadCount(resultFilter);

}

else {

endIndex = resultFilter.getNumResults() + startIndex;

}

//嘿嘿,理解了上面几句代码先,然后来看这个iterator

return new ForumThreadBlockIterator(threadBlock, query.toString(),

startIndex, endIndex, this.id, factory);

}

OK,关于(注1)

我分析了getThreadListSQL,这里是构造了一个SQL查询语句,这个语句前面的一些内容是:

SELECT jiveThread.threadID FROM jiveThread,xxxxx

那些xxxxx无非是一些查询条件,是根据resultFilter中的查询条件而自动生成的。

原来,如果执行了这个SQL的作用只能取回满足条件的thread的ID号。

(注2)getBlock()

我对这里的Block的理解是:指满足查询条件的查询结果经过排序以后的序号集合,和数据库中的threadID是不一样的。

这里的starIndex就是数组,也就是那个Block的下标,而里边的内容(Long object),是真正的threadID

代码里边对于每Block数组的大小,是固定的400。

举例来说:

如果一次查询返回1000条纪录,我要从第510条开始(startIndex=510),那么就是取第二块block(blockID=1),块起始位置(blockStart)是

500。

int blockID = startIndex / BLOCK_SIZE;

int blockStart = blockID * BLOCK_SIZE;

如果你还有耐性看到这里,并且接着读jive代码的话,会发现,加下来无非就是从Cache中取该Block数组,如果没有找到,则老老实实的从数据库中取出来,老老实实的放到数组里边,放到Cache,最后扔出这个数组。

这里的数据库处理代码比较普通

con = ConnectionManager.getConnection();

stmt = ConnectionManager.createScrollableStatement(con);

// Set the maximum number of rows to end at the end of this block.

ConnectionManager.setMaxRows(stmt, BLOCK_SIZE * (blockID+1));

ResultSet rs = stmt.executeQuery(query);

// Grab BLOCK_SIZE rows at a time.

ConnectionManager.setFetchSize(rs, BLOCK_SIZE); //一次最多取BLOCK_SIZE条,也就是400条

// Position the cursor right before the first row that we're insterested in.

ConnectionManager.scrollResultSet(rs, blockStart);// 滚动游标到blockStart

// Keep reading results until the result set is exhausted or

// we come to the end of the block.

int count = 0;

while (rs.next() && count < BLOCK_SIZE) {

objectList.add(rs.getLong(1));

count++;

}

看到这里,我忽然领悟到,这里的代码也只是复制而已,可是比我以前的复制到enumeration中要高级了好多。

首先,这里限制了jdbc返回结果集的大小。

其次,复制的内容只是一个“指针”(嘿嘿,我喜欢用这个词语)。

这样就比较好的解决了上面的速度慢和浪费内存的问题。

(C)嘿嘿,接下来的事情相对就很简单了。

1.根据startIndex计算出在该Block中的相对位置,也就是这个BlockArray中的下标啦,然后取得内容,也就是真正的threadID啦。

2.到threadCache中去取该threadObject,如果找不到,则老老实实的new thread(threadID),从数据库中去取了。

这一切都是通过ForumThreadIterator中的hasNext()/next()/getElement()来实现的。大家自己去看一下就很容易搞清楚啦。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有