捲動

捲動是一種更精細的方法,可迭代處理較大的結果集區塊。捲動包含穩定的排序、捲動類型 (基於偏移量或鍵集的捲動) 和結果限制。您可以使用屬性名稱定義簡單的排序表達式,並透過查詢衍生使用 TopFirst 關鍵字 定義靜態結果限制。您可以串連表達式以將多個條件收集到一個表達式中。

捲動查詢會回傳 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());

ScrollPosition 識別元素在整個查詢結果中的確切位置。查詢執行將位置參數視為排除,結果將從給定位置之後開始。 ScrollPosition#offset()ScrollPosition#keyset() 作為 ScrollPosition 的特殊化身,指示捲動操作的開始。

以上範例顯示靜態排序和限制。您可以選擇性地定義接受 Sort 物件的查詢方法,以定義更複雜的排序順序或依請求排序。以類似的方式,提供 Limit 物件可讓您依請求定義動態限制,而不是套用靜態限制。請在查詢方法詳細資訊中閱讀更多關於動態排序和限制的資訊。

WindowIterator 提供了一個實用工具,可簡化跨 Window 的捲動,方法是移除檢查下一個 Window 是否存在的需要,並應用 ScrollPosition

WindowIterator<User> users = WindowIterator.of(position -> repository.findFirst10ByLastnameOrderByFirstname("Doe", position))
  .startingAt(ScrollPosition.offset());

while (users.hasNext()) {
  User u = users.next();
  // consume the user
}

使用偏移量捲動

類似於分頁,偏移量捲動使用偏移量計數器來跳過一些結果,並讓資料來源僅回傳從給定偏移量開始的結果。這種簡單的機制避免將大型結果傳送到用戶端應用程式。但是,大多數資料庫都需要在您的伺服器可以回傳結果之前實現完整的查詢結果。

範例 1. 將 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 的元素。

ScollPosition.offset()ScollPosition.offset(0L) 之間存在差異。前者表示捲動操作的開始,指向沒有特定偏移量,而後者識別結果的第一個元素 (在位置 0)。鑑於捲動的排除性質,使用 ScollPosition.offset(0) 會跳過第一個元素,並轉換為偏移量 1

使用鍵集篩選捲動

基於偏移量的方法需要大多數資料庫在您的伺服器可以回傳結果之前實現整個結果。因此,雖然用戶端僅看到請求結果的一部分,但您的伺服器需要建立完整的結果,這會導致額外的負載。

鍵集篩選方法透過利用資料庫的內建功能來處理結果子集擷取,旨在減少個別查詢的計算和 I/O 需求。此方法維護一組索引鍵,以透過將索引鍵傳遞到查詢中來恢復捲動,從而有效地修改您的篩選條件。

鍵集篩選的核心概念是開始使用穩定的排序順序擷取結果。一旦您想要捲動到下一個區塊,您將取得 ScrollPosition,用於重建排序結果中的位置。 ScrollPosition 擷取目前 Window 中最後一個實體的鍵集。為了執行查詢,重建會重寫條件子句以包含所有排序欄位和主索引鍵,以便資料庫可以利用潛在的索引來執行查詢。資料庫只需要從給定的鍵集位置建構一個小得多的結果,而無需完全實現大型結果,然後跳過結果直到達到特定偏移量。

鍵集篩選要求鍵集屬性 (用於排序的屬性) 不可為 Null。此限制的應用是因為儲存特定比較運算子的 null 值處理,以及對索引來源執行查詢的需求。對可為 Null 的屬性進行鍵集篩選將導致意外的結果。

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 順序時,包含與您的查詢相關的排序屬性就足夠了;如果您不想確保查詢結果的唯一性,則不需要這樣做。鍵集查詢機制透過包含主索引鍵 (或複合主索引鍵的任何剩餘部分) 來修改您的排序順序,以確保每個查詢結果都是唯一的。