1. 概述

Spring Data Commons 是Spring Data项目的一部分,它包含管理持久层的接口和实现。滚动API是Spring Data Commons提供的功能之一,用于处理从数据库读取的大结果集。

在这个教程中,我们将一起探索滚动API及其示例。

2. 依赖

滚动API在Spring Boot 3.1版本中添加支持。Spring Data Commons已经包含在了Spring Data JPA中。因此,只需添加Spring Data JPA 3.1版本即可获得滚动API的功能:

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>3.1.0</version>
</dependency>

最新的库版本可以在Maven中央仓库找到。

3. 实体类

为了演示目的,我们将使用BookReview实体,它包含了各个用户对不同书籍的评论评级:

@Entity
@Table(name="BOOK_REVIEWS")
public class BookReview {
    @Id
    @GeneratedValue(strategy= GenerationType.SEQUENCE, generator = "book_reviews_reviews_id_seq")
    @SequenceGenerator(name = "book_reviews_reviews_id_seq", sequenceName = "book_reviews_reviews_id_seq", allocationSize = 1)
    private Long reviewsId;
    private String userId;
    private String isbn;
    private String bookRating;

    // getters and setters
}

4. 滚动API

滚动API提供了按块迭代大量结果的功能。 它提供稳定的排序、滚动类型和结果限制。

我们可以通过使用属性名称定义简单的排序表达式,并通过查询衍生法使用TopFirst来定义静态结果限制。

4.1. 使用偏移过滤滚动

下面的示例中,我们使用查询衍生方法,根据评分参数和OffsetScrollPosition找到前五个书籍:

public interface BookRepository extends Repository<BookReview, Long> {
    Window<BookReview> findFirst5ByBookRating(String bookRating, OffsetScrollPosition position);
    Window<BookReview> findFirst10ByBookRating(String bookRating, OffsetScrollPosition position);
    Window<BookReview> findFirst3ByBookRating(String bookRating, KeysetScrollPosition position);
}

既然我们定义了Repository方法,可以在逻辑类中使用它们来获取前五个书籍并持续迭代,直到达到最后一个结果。

在迭代过程中,我们需要查询是否存在下一个窗口:

public List<BookReview> getBooksUsingOffset(String rating) {
    OffsetScrollPosition offset = ScrollPosition.offset();

    Window<BookReview> bookReviews = bookRepository.findFirst5ByBookRating(rating, offset);
    List<BookReview> bookReviewsResult = new ArrayList<>();
    do {
        bookReviews.forEach(bookReviewsResult::add);
        bookReviews = bookRepository.findFirst5ByBookRating(rating, (OffsetScrollPosition) bookReviews.positionAt(bookReviews.size() - 1));
    } while (!bookReviews.isEmpty() && bookReviews.hasNext());

    return bookReviewsResult;
}

我们可以简化逻辑,使用WindowIterator,它提供了无需检查下一个窗口和ScrollPosition就能浏览大结果的便利工具:

public List<BookReview> getBooksUsingOffSetFilteringAndWindowIterator(String rating) {
    WindowIterator<BookReview> bookReviews = WindowIterator.of(position -> bookRepository
      .findFirst5ByBookRating("3.5", (OffsetScrollPosition) position)).startingAt(ScrollPosition.offset());
    List<BookReview> bookReviewsResult = new ArrayList<>();
    bookReviews.forEachRemaining(bookReviewsResult::add);

    return bookReviewsResult;
}

基于偏移的滚动工作方式类似于分页,它跳过大量结果中的某些记录返回预期结果。虽然我们只看到请求结果的一部分,但服务器需要构建完整的结果,这会增加额外的负载。

我们可以使用键集过滤来避免这种行为。

4.2. 使用键集过滤滚动

键集过滤利用数据库的内置能力帮助检索子集结果,旨在减少单个查询的计算和IO需求

数据库只需要根据给定的键集位置构造较小的结果,而不需要materialize整个大结果:

public List<BookReview> getBooksUsingKeySetFiltering(String rating) {
    WindowIterator<BookReview> bookReviews = WindowIterator.of(position -> bookRepository
      .findFirst5ByBookRating(rating, (KeysetScrollPosition) position))
      .startingAt(ScrollPosition.keyset());
    List<BookReview> bookReviewsResult = new ArrayList<>();
    bookReviews.forEachRemaining(bookReviewsResult::add);

    return bookReviewsResult;
}

5. 总结

在这篇文章中,我们探讨了Spring Data Commons库提供的滚动API。滚动API支持基于偏移位置和过滤条件分块读取大结果。

滚动API支持基于偏移和键集的过滤。 基于偏移的过滤需要在数据库中materialize整个结果,而键集则通过构造较小结果来减轻数据库的计算和IO负担。

如往常一样,示例代码可在GitHub上找到