Blaze-Persistence附带了对分页的大力支持。在这篇博文中,我描述了支持的不同分页模式以及如何使用 Blaze-Persistence 实现它们。
分页类型
Blaze-Persistence支持以下分页模式:
-
偏移分页
-
键集分页
偏移分页
将分页数据想象成具有开头和结尾的元素数组。例如,此数组可能是数据库中要按页显示的表的内容。偏移分页的工作原理是指定从指示当前页开始的数组开头的偏移量。当用户在数组中分页时将元素插入数组时,这可能会有问题。
例如,请考虑以下数组:
假设页面大小为 3,当前页面大小为 2(从 1 开始)。因此,当前页面是:
当用户查看第 2 页时,假设元素入到列表中,紧接着:fo
然后,用户加载下一页。在内部,这可以通过访问偏移量为 of 的数据数组来实现。生成的页面为:cur_offset + pagesize = 6
因此,这种类型的分页的问题在于,随着新元素的插入,现有元素会在页面之间移动。从用户的角度来看,这看起来数据中有两个实例。a
为了解决这种情况,我们需要以某种方式观察新元素的插入。然后,我们需要检查新元素的位置是否小于:cur_offset + pagesize
-
减:增量cur_offset
-
大于或等于:无需执行任何操作
根据您的数据存储,对新元素的这种观察可能很难实现且不可靠。对于数据分页等看似微不足道的用例,这肯定是矫枉过正。
偏移分页固有的另一个缺点来自性能角度。由于我们根据列表开头的偏移量显示单个页面,因此我们需要知道所需页面之前的所有数据。因此,从数据库获取时,数据库必须在内部计算完整的结果集,包括所需的页面,以便随后能够以有意义的方式应用偏移值和页面大小。
键集分页以救援
当相对于最后一页中的元素 ID 执行分页时,可以防止偏移分页面临的问题。
让我们通过为列表元素分配唯一 ID 来调整前面的示例:
同样,第 2 页是:
插入列表后,列表如下所示:f(8)
当驻留在第 2 页上时,下一页现在根据第 2 页中的最后一个 ID 和页面大小确定。在SQL术语中,我们只应用一个约束。基于这个逻辑,第 3 页现在是:where id > last_id limit page_size
同样重要的是要注意,我们不需要了解第 3 页之前的完整元素列表即可构建页面。简而言之,使用键集分页时,分页的有效性和效率都会得到提高。
您可能还想查看这篇关于键集分页的精彩博客文章- 请注意它是德语的。
代码示例
现在您已经了解了不同分页模式背后的想法,我将向您展示如何使用 Blaze-Persistence 进行分页。Blaze-Persistence 提供了 2 个 API,具体取决于您是否使用实体视图。
通常,使用这两个 API,您最终会得到一个返回 awhen calling。提供对可用于后续查询中键集分页的 ID 集的访问。PaginatedCriteriaBuilderPagedListgetResultList()PagedList#getKeysetPage()
没有实体视图的分页
默认的 CriteriaBuilder 提供了三种分页的 page() 方法:
page(int firstResult, int maxResults)
此方法执行简单偏移量 pagination.is 相当于上一节的偏移量,并与页面大小相对应。firstResultmaxResults
page(Object entityId, int maxResults)
使用此方法导航到包含具有 ID 的特定实体的页面。如果存在具有给定 ID 的实体,则返回的页面将以此实体开头。entityId
page(KeysetPage keysetPage, int firstResult, int maxResults)
这是键集分页的入口点。参数是从以前的查询结果中提取的 ID 集。如果选择参数使得无法进行键集分页,则会执行对偏移分页的透明回退。keysetPagePagedList#getKeysetPage()firstResult
// initial query using offset pagination
PagedList<Cat> page3 = cbf.create(em, Cat.class)
.orderByAsc("id") // unique ordering is required for pagination
.page(6, 3)
.withKeysetExtraction(true)
.getResultList();
// keyset pagination for querying the next page
PagedList<Cat> page4 = cbf.create(em, Cat.class)
.orderByAsc("id")
.page(page3.getKeysetPage(), 9, 3)
.withKeysetExtraction(true)
.getResultList();
// keyset pagination for querying the previous page
PagedList<Cat> page2 = cbf.create(em, Cat.class)
.orderByAsc("id")
.page(page3.getKeysetPage(), 3, 3)
.withKeysetExtraction(true)
.getResultList();
使用实体视图进行分页
Blaze-Persistence允许通过方法与实体视图结合使用分页。由于 API 类似于 API,因此我将跳过 API 描述,直接跳转到代码示例。EntityViewSetting#createCriteriaBuilder#page()
CriteriaBuilder<Cat> baseQueryBuilder = cbf.create(em, Cat.class)
.orderByAsc("id");
// initial query using offset pagination
EntityViewSetting<CatView, PaginatedCriteriaBuilder<CatView> setting =
EntityViewSetting.create(CatView.class, 6, 3);
PagedList<CatView> list1 = evm.applySetting(setting, baseQueryBuilder)
.withKeysetExtraction(true)
.getResultList();
// keyset pagination for querying the next page
EntityViewSetting<CatView, PaginatedCriteriaBuilder<CatView> setting =
EntityViewSetting.create(CatView.class, 9, 3);
PagedList<CatView> list2 = evm.applySetting(setting, baseQueryBuilder)
.withKeysetExtraction(true)
.getResultList();
// keyset pagination for querying the previous page
EntityViewSetting<CatView, PaginatedCriteriaBuilder<CatView> setting =
EntityViewSetting.create(CatView.class, 3, 3);
PagedList<CatView> list3 = evm.applySetting(setting, baseQueryBuilder)
.withKeysetExtraction(true)
.getResultList();
有关更多示例,请查看GitHub 上的展示。
结论
这篇博文解释了 Blaze-Persistence 支持的 2 种分页模式,并提供了具体的使用示例。分页几乎在任何应用程序中都是一个问题,我希望本文可以为您提供有关如何正确分页的见解。如有疑问,请在Slack上联系我们或发表评论。