Pagination, sortering og slice-streaming
Returnér paginerede, sorterede og streamede resultatsæt effektivt til store datasæt og brugergrænseflader med uendelig scroll.
Pagination, sortering og slice-streaming er en gratis Komplet guide til Spring Boot 4-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Komplet guide til Spring Boot 4, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Komplet guide til Spring Boot 4-kurset indeholder 4 lektioner i alt.
Hvorfor sideopdeling er vigtig
At indlæse en hel tabel i hukommelsen er en af de hurtigste måder at få en tjeneste til at gå ned på. Et findAll() på en tabel med en million rækker opbygger en enorm liste, tømmer heapen og blokerer tråden, der håndterer forespørgslen.
Sideopdeling løser dette ved at hente resultater i små bidder (sider). Spring Data JPA giver førsteklasses understøttelse via abstraktionen Pageable.
- Page — kender det samlede antal elementer og sider.
- Slice — ved kun, om der findes en næste bid (billigere).
- Stream — behandler rækker én ad gangen uden at materialisere en liste.
I denne lektion lærer du, hvornår du skal vælge hver af dem.
Pageable-parameteren
Tilføj en Pageable-parameter til en vilkårlig repository-metode, så tilføjer Spring Data automatisk LIMIT og OFFSET (eller den tilsvarende del af SQL-dialekten) til den genererede forespørgsel.
Metoden returnerer en Page<T>, som omslutter indholdslisten samt metadata om sideopdelingen.
public interface ProductRepository extends JpaRepository<Product, Long> {
Page<Product> findByCategory(String category, Pageable pageable);
}Opbygning af en PageRequest
Den konkrete implementering af Pageable er PageRequest. Du opretter en med et sidenummer, der starter ved nul, og en sidestørrelse.
PageRequest.of(0, 20)— første side, 20 elementer.PageRequest.of(2, 20)— tredje side (rækkerne 40-59).
I en controller lader du normalt Spring løse den ud fra forespørgslen automatisk, men du kan også opbygge den manuelt i en service.
@Service
public class ProductService {
private final ProductRepository repository;
public ProductService(ProductRepository repository) {
this.repository = repository;
}
public Page<Product> firstPage(String category) {
Pageable pageable = PageRequest.of(0, 20);
return repository.findByCategory(category, pageable);
}
}Læsning af sidemetadata
En Page<T> stiller alt til rådighed, som en brugergrænseflade skal bruge for at vise kontrolelementer til sideopdeling.
getContent()— rækkerne på denne side.getTotalElements()— det samlede antal matchende rækker i tabellen.getTotalPages()— det samlede antal sider.getNumber()— det aktuelle sidenummer, der starter ved nul.hasNext()/hasPrevious()— navigationsflag.
Vigtigt: Beregning af getTotalElements() kræver en ekstra COUNT(*)-forespørgsel ved hvert kald.
Page<Product> page = repository.findByCategory("books", PageRequest.of(0, 20));
List<Product> rows = page.getContent();
long total = page.getTotalElements();
int totalPages = page.getTotalPages();
boolean more = page.hasNext();Sortering med Sort
Sortering er en del af Pageable. Giv et Sort-objekt til PageRequest.of(page, size, sort), så tilføjer Spring Data en ORDER BY-sætning.
Sort.by("price")— stigende efter pris.Sort.by("price").descending()— faldende.- Kæd flere sorteringer for at afgøre rækkefølgen ved ens værdier.
Sort sort = Sort.by("price").descending()
.and(Sort.by("name").ascending());
Pageable pageable = PageRequest.of(0, 20, sort);
Page<Product> page = repository.findByCategory("books", pageable);Sortering efter flere felter med Order
Hvis du vil have mere kontrol over håndtering af null-værdier og retningen for hvert felt, skal du opbygge Sort ud fra Sort.Order-objekter.
Det er den tydeligste måde at udtrykke noget som "nyeste først, derefter alfabetisk, null-værdier sidst".
Sort sort = Sort.by(
Sort.Order.desc("createdAt"),
Sort.Order.asc("name").nullsLast()
);
Pageable pageable = PageRequest.of(0, 25, sort);
Page<Product> page = repository.findAll(pageable);Slice kontra Page
En Page kører altid en ekstra COUNT(*) for at kende totalen. Til en brugergrænseflade med uendelig rulning har du sjældent brug for totalen — du skal kun vide, om der findes en næste bid.
Returnér i stedet Slice<T>. Spring henter internt size + 1 rækker: Hvis den ekstra række findes, er hasNext() sand. Der køres ingen optællingsforespørgsel, så det er mærkbart billigere på store tabeller.
Slice.getContent()ogSlice.hasNext()fungerer ligesom for Page.Slice.getTotalElements()findes ikke.
public interface FeedRepository extends JpaRepository<Post, Long> {
Slice<Post> findByAuthorId(Long authorId, Pageable pageable);
}Brug af en Slice til uendelig rulning
På klienten bliver du ved med at øge sidenummeret, så længe hasNext() er sand. Servicen forbliver enkel, fordi repositoryet håndterer tricket med size + 1.
public Slice<Post> nextChunk(Long authorId, int pageNumber) {
Pageable pageable = PageRequest.of(
pageNumber, 15, Sort.by("createdAt").descending());
Slice<Post> slice = feedRepository.findByAuthorId(authorId, pageable);
if (slice.hasNext()) {
// tell the UI to request pageNumber + 1
}
return slice;
}Problemet med offset
Offset-sideinddeling har en skjult omkostning: Hvis databasen skal returnere side 10000 med størrelsen 20, skal den gennemgå og kassere 200000 rækker, før den returnerer dine. Dybe sider bliver langsommere og langsommere.
Keyset-sideinddeling (cursor-sideinddeling) undgår dette. I stedet for OFFSET filtrerer du efter den sidst sete værdi. Forespørgslen forbliver hurtig uanset dybden, fordi den kan bruge et indeksopslag.
public interface PostRepository extends JpaRepository<Post, Long> {
@Query("SELECT p FROM Post p WHERE p.id < :lastId ORDER BY p.id DESC")
Slice<Post> findOlderThan(@Param("lastId") Long lastId, Pageable pageable);
}Streaming af store resultatsæt
Når du skal behandle hver eneste række (eksport, batchjob, rapport), men ikke kan have dem alle i hukommelsen, skal du returnere en Stream<T>. JPA læser rækker dovent fra en fremadrettet cursor.
To regler kan ikke fraviges:
- Metoden skal køre i en transaktion (
@Transactional), så cursoren forbliver åben. - Du skal lukke streamen — brug try-with-resources — for at frigive databasecursoren.
public interface OrderRepository extends JpaRepository<Order, Long> {
@QueryHints(@QueryHint(name = HINT_FETCH_SIZE, value = "100"))
@Query("SELECT o FROM Order o WHERE o.status = :status")
Stream<Order> streamByStatus(@Param("status") String status);
}Sikker tømning af en stream
Pak streamen ind i try-with-resources, og sørg for, at hele forbruget foregår i én transaktionsmetode. Frakobl eller ryd entiteter med jævne mellemrum, hvis du ændrer dem, så persistence-konteksten ikke vokser uden grænse.
@Service
public class OrderExporter {
private final OrderRepository repository;
public OrderExporter(OrderRepository repository) {
this.repository = repository;
}
@Transactional(readOnly = true)
public long exportPending(Consumer<Order> writer) {
long count = 0;
try (Stream<Order> stream = repository.streamByStatus("PENDING")) {
for (Order order : (Iterable<Order>) stream::iterator) {
writer.accept(order);
count++;
}
}
return count;
}
}Hurtig kontrol: Vælg den rigtige returtype
Du bygger et feed med uendelig rulning over en tabel med 5 millioner rækker. Brugerfladen viser aldrig et samlet antal, men kun en knap til at "indlæse flere". Hvilken returtype fra repositoryet passer bedst?
Opsummering
Du har nu en værktøjskasse til håndtering af store resultatsæt i Spring Data JPA:
- Page<T> — indhold plus samlede antal; kræver en
COUNT(*)-forespørgsel. Brug den, når brugerfladen viser det samlede antal sider. - Slice<T> — indhold plus
hasNext(); ingen optællingsforespørgsel. Bedst til uendelig rulning. - Sort — kombiner med
PageRequest.of(page, size, sort)for sorterede, deterministiske sider. - Keyset-sideinddeling — filtrer efter den sidst sete værdi for at holde dybe sider hurtige.
- Stream<T> — doven, fremadrettet behandling i
@Transactionalmed try-with-resources til meget store batchjob.
Tilpas returtypen til adgangsmønstret, så dine forespørgsler forbliver hurtige og hukommelsessikre uanset skala.
Lær Java med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 21
- Lektioner
- 84
Ofte stillede spørgsmål
Er lektionen “Pagination, sortering og slice-streaming” gratis?
Ja — alle 3 lektioner i læringssporet Komplet guide til Spring Boot 4, inklusive “Pagination, sortering og slice-streaming”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Komplet guide til Spring Boot 4-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Pagination, sortering og slice-streaming”?
Returnér paginerede, sorterede og streamede resultatsæt effektivt til store datasæt og brugergrænseflader med uendelig scroll. Du øver dig i Komplet guide til Spring Boot 4 med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Komplet guide til Spring Boot 4?
Der kræves ingen tidligere erfaring. Komplet guide til Spring Boot 4 på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.
Hvor lang tid tager lektionen “Pagination, sortering og slice-streaming”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Komplet guide til Spring Boot 4-lektion?
Ja. Alle Komplet guide til Spring Boot 4-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Afledte query-metoder og keyword-opslag
- JPQL og native queries med @Query
- Specifications og dynamisk filtrering baseret på kriterier
- Pagination, sortering og slice-streaming