滚动
滚动是一种更精细的方法,用于迭代更大的结果集块。滚动包含稳定的排序、滚动类型(基于偏移或基于键集)和结果限制。您可以通过属性名称定义简单的排序表达式,并通过查询派生使用 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
}
if (users.isLast() || users.isEmpty()) {
break;
}
// obtain the next Scroll
users = repository.findFirst10ByLastnameOrderByFirstname("Doe", users.positionAt(users.size() - 1));
} while (!users.isEmpty());
|
|
|
上面的示例展示了静态排序和限制。您可以定义接受 |
通过消耗 Window 实例进行滚动需要相当多的条件才能实现最佳的数据库往返,并且可能很快成为一个重复的任务,可以使用 WindowIterator 进行简化。
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 与 Repository 查询方法一起使用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 捕获当前 Window 中最后一个实体的键集。为了运行查询,重建会重写条件子句以包含所有排序字段和主键,以便数据库可以利用潜在的索引来运行查询。数据库只需要从给定键集位置构建一个更小的结果,而无需完全具体化大量结果,然后跳过结果直到达到特定偏移量。
|
键集过滤要求键集属性(用于排序的属性)不可为空。此限制适用于存储特定的比较运算符的 |
KeysetScrollPosition 与 Repository 查询方法一起使用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 顺序时,包含与您的查询相关的排序属性就足够了;如果您不想,则无需确保唯一的查询结果。键集查询机制通过包含主键(或复合主键的任何剩余部分)来修改您的排序顺序,以确保每个查询结果都是唯一的。