滾動
滾動 (Scrolling) 是一種更細粒度的方法,用於迭代大型結果集塊。滾動包括一個穩定排序、一種滾動型別(基於 Offset 或 Keyset 的滾動)和結果限制。您可以使用屬性名稱定義簡單的排序表示式,並使用查詢派生透過 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
s 的滾動。
WindowIterator<User> users = WindowIterator.of(position -> repository.findFirst10ByLastnameOrderByFirstname("Doe", position))
.startingAt(ScrollPosition.offset());
while (users.hasNext()) {
User u = users.next();
// consume the user
}
使用 Offset 進行滾動
Offset 滾動類似於分頁,使用 Offset 計數器跳過一定數量的結果,並讓資料來源僅返回從給定 Offset 開始的結果。這種簡單的機制避免了將大量結果傳送到客戶端應用程式。然而,大多數資料庫要求在您的伺服器返回結果之前完全實現整個查詢結果。
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 | 不帶 offset 開始,以包含位置 0 的元素。 |
|
使用 Keyset-Filtering 進行滾動
基於 Offset 的滾動要求大多數資料庫在您的伺服器返回結果之前實現整個結果。因此,雖然客戶端只看到請求結果的一部分,但您的伺服器需要構建完整結果,這會增加額外的負載。
Keyset-Filtering 透過利用資料庫的內建能力來獲取結果子集,旨在減少單個查詢的計算和 I/O 需求。這種方法透過將鍵傳遞到查詢中來維護一組鍵以恢復滾動,從而有效地修改您的過濾條件。
Keyset-Filtering 的核心思想是使用穩定的排序順序開始檢索結果。當您想要滾動到下一個塊時,會獲得一個 ScrollPosition
,用於重建排序結果中的位置。ScrollPosition
捕獲當前 Window
中最後一個實體的 keyset。為了執行查詢,重建過程會重寫條件子句,以包含所有排序欄位和主鍵,這樣資料庫就可以利用潛在的索引來執行查詢。資料庫只需根據給定的 keyset 位置構建一個更小的結果,而無需完全實現大型結果,然後跳過結果直到達到特定 offset。
Keyset-Filtering 要求 keyset 屬性(用於排序的屬性)不可為 null。此限制是由於特定儲存的比較運算子對 |
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 | 從最開始處啟動,不應用額外的過濾。 |
當資料庫包含與排序欄位匹配的索引時,Keyset-Filtering 效果最佳,因此靜態排序效果很好。應用 Keyset-Filtering 的滾動查詢要求查詢返回排序順序中使用的屬性,並且這些屬性必須對映到返回的實體中。
您可以使用介面和 DTO 投影,但請確保包含所有您已排序的屬性,以避免 keyset 提取失敗。
指定 Sort
順序時,只需包含與您的查詢相關的排序屬性即可;如果您不希望,則無需確保查詢結果是唯一的。keyset 查詢機制透過包含主鍵(或複合主鍵的任何其餘部分)來修正您的排序順序,以確保每個查詢結果都是唯一的。