如何在Java中分页处理大的结果集是最佳方式?

4
我希望您能够从性能的角度出发,提供最佳方法来部分地在网页上显示Resultset,比如每页10个项目,如果用户想要查看更多结果,则按下“下一页”按钮。 我认为(可能是错误的),当按下“下一页”按钮时,应该向服务器发送新的请求?
目前,我正在尝试学习Java,GWT。
谢谢!
PS:对于我的英语,我很抱歉。
6个回答

4
答案取决于您的用户行为:他们会多频繁地查看第2页、第10页或第100页。
如果他们很少查看第2页,从不查看第10页或第100页,则重新提交请求可能是可以的。
如果他们通常查看第2页,经常查看第10页,偶尔查看第100页,则部分缓存将非常有用:缓存前100(或200、300)个结果,并且只在他们超过这些结果时重新提交查询。我可能会将缓存存储在用户的会话中,但如果您的应用程序服务器是集群的,则必须考虑一些问题。
而如果他们总是浏览每个结果呢?部分缓存仍然是答案,因为您不希望将大块数据存储在内存中。

1

既然你的标签中有"GWT",我会认为你的服务器应用正在运行于Google App Engine(GAE)上。

  • 一种方法是在第一次查询时获取所有结果,将它们存储在数据库中,展示前20个,然后让下一页/上一页链接从数据库中拉取子集数据。但在用户会话超时时,必须记得从数据库中删除那些结果!

  • 另一种方法是在每个页面视图中获取所有结果,但跳过结果直到找到所需的20个子集,然后仅输出这些结果。

我认为在GAE下,第二种方法会更好,除非你的查询可能返回超过1000个结果,在一次交易中GAE不能让你检索那么多。

  • 如果你的数据和键值本身就适合这样做,最好的方法是在查询时即提取出正确的20个数据项。但是除非你的数据具有连续递增的整数键,否则这可能很难实现。

+1 对于应用引擎的评论...我认为GWT只是被用作普通应用服务器的前端。 - kdgregory

0
通常你只从数据库中获取一个“页面”。
假设有一个查询。
select * from mytable where column1="a";

这会提供1000条记录。然后获取页面类似于(mysql):

select * from mytable where column1="a" limit 0, 10;

对于第一页(0到10),第二页将会像这样被获取:

select * from mytable where column1="a" limit 10, 20;

如果数据量较大 (1000 条记录),但不是超级大 (100 万条记录),您也可以一次性提供整个数据集并使用 JavaScript 进行分页。这样做的优点是可以在客户端进行排序。

1
LIMIT在所有SQL方言中都不可用,并且根据查询结构的不同,它可能非常低效--引擎将不得不执行完整的查询,然后应用限制(尽管MySQL确实有优化)。 - kdgregory
如果您使用具有足够大的查询缓存的MySQL,它并不那么昂贵,并且可以很好地处理大量客户端(如果缓存已满,则会牺牲CPU)。根据给定的信息,我真的想不到更好的解决方案,除了可能是一个单独的、动态创建的表,这将需要大量容错清理代码。 - extraneon

0

如果由于内存限制而无法使用基于缓存的方法,请使用基于查询的方法。调整搜索查询的WHERE子句,根据用户请求的页面明确选择数据。这种方法需要在页面请求中来回传递附加的上下文信息。

一种方法是使用逻辑行ID(或主键)获取页面,以界定页面并识别结果集中的每一行。

假设您有一个非常简单的表格,其中包含一系列数字顺序的行ID。如果您每页显示100行,并且用户请求了第二页,您将如下调整WHERE子句:

select col, col2 from my_table where
row_id > 100
and row_id <= 200
order by rownum asc

0
如果您正在使用在GAE上运行良好的JPA,则可以使用以下方式对结果集进行分页:
Query#setFirstResult(int startPosition)
Query#setMaxResults(int maxResult)
此文章可能会有所帮助:Paging large data sets with a LazyList

0

您可以在Web层、后端层(例如EJB)或数据库层(作为最后的“限制”或row_id语句)中缓存/检索记录。您应该使用哪种方法取决于您的要求(如kdgregory所说)。

最流行的方法是使用会话在Web层缓存它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接