1、概览 {#1概览}
Spring Data Commons 是总括 Spring Data project 的一部分,其中包含管理持久层的接口和实现。Scroll API 是 Spring Data Commons 提供的功能之一,用于处理从数据库读取的大型结果。
在本教程中,我们将通过一个示例探索 Scroll API。
2、依赖 {#2依赖}
Spring Boot 3.1 版本新增了 Scroll API 支持。Spring Data Commons 已包含在 Spring Data JPA 中。因此,添加 Spring Data JPA 3.1 版本就可以获得 Scroll API 功能:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>3.1.0</version>
</dependency>
最新的版本可在 Maven 中央仓库 中找到。
3、 Entity 类 {#3-entity-类}
例如,我们将使用 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;
// getter 和 setter 方法
}
4、Scroll API {#4scroll-api}
Scroll API 提供了分块迭代大型结果的功能。它提供稳定的排序、scroll 类型和结果限制。
我们可以使用属性名称定义简单的排序表达式,并通过查询推导使用 Top
或 First
定义静态结果限制。
4.1、使用 Offset 过滤 {#41使用-offset-过滤}
在下面的示例中,我们使用查询派生功能,根据评分参数和 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 方法,我们就可以在业务类中使用它们来获取前五本图书,并不断迭代直到得到最后一个结果。
在迭代过程中,我们需要通过查询来检查下一个 Window 是否存在:
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
来简化我们的逻辑,通过使用这个工具类,可以不用检查下一个 window 和 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;
}
Offset Scroll 的工作原理类似于分页,通过跳过大结果中一定数量的记录来返回预期结果。虽然我们只能看到所请求结果的一部分,但服务器却需要构建完整的结果,这就造成了额外的负载。
我们可以使用 keyset 过滤功能来避免这种行为。
4.2、使用 Keyset 过滤 {#42使用-keyset-过滤}
Keyset 过滤有助于利用数据库的内置功能检索结果子集,从而减少单个查询的计算量和 IO 需求。
数据库只需要从给定的 keyset 位置构建较小的结果,而不需要生成大量的完整结果:
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、总结 {#5总结}
在本文中,我们探讨了 Spring Data Commons 库提供的 Scroll API。Scroll API 支持根据偏移位置和筛选条件将大结果分成小块读取。
Scroll API 支持使用 offset 和 keyset 进行过滤。基于 offset 的过滤需要在数据库中具体化整个结果,而 keyset 则通过构建较小的结果来帮助减少数据库的计算和 IO 负载。
参考:https://www.baeldung.com/spring-data-jpa-scroll-api