滚动
滚动是一种更细粒度的方法,用于遍历较大的结果集块。滚动包含一个稳定排序、一个滚动类型(基于偏移或键集的滚动)和结果限制。您可以使用属性名称定义简单的排序表达式,并使用 Top
或 First
关键字 通过查询派生定义静态结果限制。您可以连接表达式以将多个条件收集到一个表达式中。
滚动查询返回一个 Window<T>
,它允许获取元素的滚动位置以获取下一个 Window<T>
,直到您的应用程序使用完整个查询结果。类似于通过获取下一批结果来使用 Java Iterator<List<…>>
,查询结果滚动允许您通过 Window.positionAt(…)
访问 ScrollPosition
。
Window<User> users = repository.findFirst10ByLastnameOrderByFirstname("Doe", ScrollPosition.offset());
do {
for (User u : users) {
// consume the user
}
// obtain the next Scroll
users = repository.findFirst10ByLastnameOrderByFirstname("Doe", users.positionAt(users.size() - 1));
} while (!users.isEmpty() && users.hasNext());
|
WindowIterator
提供了一个实用工具,通过消除检查下一个 Window
的存在并应用 ScrollPosition
的需要,来简化跨 Window
的滚动。
WindowIterator<User> users = WindowIterator.of(position -> repository.findFirst10ByLastnameOrderByFirstname("Doe", position))
.startingAt(ScrollPosition.offset());
while (users.hasNext()) {
User u = users.next();
// consume the user
}
使用偏移量滚动
偏移量滚动使用类似于分页的方法,即偏移量计数器来跳过多个结果,并让数据源仅从给定的偏移量开始返回结果。这种简单的机制避免了将大量结果发送到客户端应用程序。但是,大多数数据库在服务器可以返回结果之前,都需要具体化完整的查询结果。
OffsetScrollPosition
与存储库查询方法结合使用interface UserRepository extends Repository<User, Long> {
Window<User> findFirst10ByLastnameOrderByFirstname(String lastname, OffsetScrollPosition position);
}
WindowIterator<User> users = WindowIterator.of(position -> repository.findFirst10ByLastnameOrderByFirstname("Doe", position))
.startingAt(OffsetScrollPosition.initial()); (1)
1 | 从无偏移量开始,以包含位置 0 处的元素。 |
|
使用键集过滤滚动
基于偏移量的滚动要求大多数数据库在服务器可以返回结果之前,具体化整个结果。因此,虽然客户端仅看到请求结果的一部分,但服务器需要构建完整的结果,这会导致额外的负载。
键集过滤通过利用数据库的内置功能来实现结果子集检索,旨在减少单个查询的计算和 I/O 需求。此方法维护一组键,以便通过将键传递到查询中来恢复滚动,从而有效地修改筛选条件。
键集过滤的核心思想是从使用稳定的排序顺序开始检索结果。一旦要滚动到下一块,便会获取一个 ScrollPosition
,该 ScrollPosition
用于重建已排序结果中的位置。ScrollPosition
捕获当前 Window
中最后一个实体的键集。为了运行查询,重建将重写条件子句以包括所有排序字段和主键,以便数据库可以利用潜在索引来运行查询。数据库只需要从给定的键集位置构建一个更小的结果,而无需完全具体化一个较大的结果,然后跳过结果,直到达到特定偏移量。
键集筛选要求键集属性(用于排序的那些属性)为非空。此限制适用于存储特定的 |
KeysetScrollPosition
与存储库查询方法结合使用interface UserRepository extends Repository<User, Long> {
Window<User> findFirst10ByLastnameOrderByFirstname(String lastname, KeysetScrollPosition position);
}
WindowIterator<User> users = WindowIterator.of(position -> repository.findFirst10ByLastnameOrderByFirstname("Doe", position))
.startingAt(ScrollPosition.keyset()); (1)
1 | 从头开始,不应用其他筛选。 |
键集筛选在数据库包含与排序字段匹配的索引时效果最佳,因此静态排序效果很好。应用键集筛选的滚动查询要求按排序顺序使用的属性由查询返回,并且这些属性必须在返回的实体中映射。
可以使用接口和 DTO 投影,但请务必包括已按其排序的所有属性,以避免键集提取失败。
指定 Sort
顺序时,只需包括与查询相关的排序属性即可;如果您不想确保查询结果唯一,则无需这样做。键集查询机制通过包括主键(或复合主键的任何剩余部分)来修改您的排序顺序,以确保每个查询结果都是唯一的。