Spring Boot 4 Complete Guide · درس

الترقيم والترتيب وبث الشرائح

إرجاع مجموعات نتائج مُرقّمة ومرتبة ومتدفقة بكفاءة لمجموعات البيانات الكبيرة وواجهات التمرير اللانهائي.

الدرس 4 من 413 خطوة

الترقيم والترتيب وبث الشرائح درس مجاني في Spring Boot 4 Complete Guide على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Spring Boot 4 Complete Guide، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Spring Boot 4 Complete Guide 4 دروس في المجموع.

بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.

Why Pagination Matters

Loading an entire table into memory is one of the fastest ways to crash a service. A findAll() on a million-row table builds a giant list, exhausts the heap, and blocks the request thread.

Pagination solves this by fetching results in small chunks (pages). Spring Data JPA gives you first-class support through the Pageable abstraction.

  • Page — knows the total element count and total page count.
  • Slice — only knows whether a next chunk exists (cheaper).
  • Stream — processes rows one-by-one without materializing a list.

In this lesson you will learn when to reach for each one.

The Pageable Parameter

Add a Pageable parameter to any repository method and Spring Data appends LIMIT and OFFSET (or the dialect equivalent) to the generated query automatically.

The method returns a Page<T>, which wraps the content list plus paging metadata.

public interface ProductRepository extends JpaRepository<Product, Long> {

    Page<Product> findByCategory(String category, Pageable pageable);
}

Building a PageRequest

The concrete implementation of Pageable is PageRequest. You create one with a zero-based page number and a page size.

  • PageRequest.of(0, 20) — first page, 20 items.
  • PageRequest.of(2, 20) — third page (rows 40-59).

In a controller you usually let Spring resolve it from the request automatically, but you can also build it by hand in a 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);
    }
}

Reading Page Metadata

A Page<T> exposes everything a UI needs to render pagination controls.

  • getContent() — the rows on this page.
  • getTotalElements() — total matching rows in the table.
  • getTotalPages() — total number of pages.
  • getNumber() — current zero-based page index.
  • hasNext() / hasPrevious() — navigation flags.

Important: computing getTotalElements() requires an extra COUNT(*) query on every call.

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();

Sorting with Sort

Sorting is part of Pageable. Pass a Sort object into PageRequest.of(page, size, sort) and Spring Data adds an ORDER BY clause.

  • Sort.by("price") — ascending by price.
  • Sort.by("price").descending() — descending.
  • Chain multiple orders for tie-breaking.
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);

Multi-Field Sort with Order

For finer control over null handling and direction per field, build the Sort from Sort.Order objects.

This is the clearest way to express something like "newest first, then alphabetical, nulls last".

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 vs Page

A Page always runs an extra COUNT(*) to know the total. For an infinite-scroll UI you rarely need the total — you only need to know if there is a next chunk.

Return Slice<T> instead. Spring fetches size + 1 rows internally: if the extra row exists, hasNext() is true. No count query runs, so it is noticeably cheaper on large tables.

  • Slice.getContent() and Slice.hasNext() work just like Page.
  • Slice.getTotalElements() does not exist.
public interface FeedRepository extends JpaRepository<Post, Long> {

    Slice<Post> findByAuthorId(Long authorId, Pageable pageable);
}

Consuming a Slice for Infinite Scroll

On the client you keep incrementing the page number while hasNext() stays true. The service stays clean because the repository handles the size + 1 trick.

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;
}

The Offset Problem

Offset pagination has a hidden cost: to return page 10000 with size 20, the database must scan and discard 200000 rows before returning yours. Deep pages get slower and slower.

Keyset (cursor) pagination avoids this. Instead of OFFSET, you filter on the last seen value. The query stays fast at any depth because it can use an index seek.

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 Large Result Sets

When you must process every row (export, batch job, report) but cannot hold them all in memory, return a Stream<T>. JPA reads rows lazily from a forward-only cursor.

Two rules are non-negotiable:

  • The method must run inside a transaction (@Transactional) so the cursor stays open.
  • You must close the stream — use try-with-resources — to release the DB cursor.
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);
}

Draining a Stream Safely

Wrap the stream in try-with-resources and keep the whole consumption inside one transactional method. Detach or clear entities periodically if you mutate them, so the persistence context does not grow unbounded.

@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;
    }
}

Quick Check: Choosing the Right Return Type

You are building an infinite-scroll feed over a 5-million-row table. The UI never shows a total count, only a "load more" button. Which repository return type is the best fit?

Recap

You now have a toolbox for handling large result sets in Spring Data JPA:

  • Page<T> — content plus total counts; pays for a COUNT(*) query. Use when the UI shows total pages.
  • Slice<T> — content plus hasNext(); no count query. Best for infinite scroll.
  • Sort — combine with PageRequest.of(page, size, sort) for ordered, deterministic pages.
  • Keyset pagination — filter on the last seen value to keep deep pages fast.
  • Stream<T> — lazy, forward-only processing inside @Transactional with try-with-resources for huge batch jobs.

Match the return type to the access pattern, and your queries stay fast and memory-safe at any scale.

البدء مجانًا

تعلم Java مع معلم ذكاء اصطناعي — مجانًا

اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.

الدورات
21
الدروس
84

الأسئلة الشائعة

هل درس «الترقيم والترتيب وبث الشرائح» مجاني؟

نعم — نص درس «الترقيم والترتيب وبث الشرائح» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Spring Boot 4 Complete Guide، انتقل إلى CoddyKit PRO. تتضمن دورة Spring Boot 4 Complete Guide 4 دروس في المجموع.

ماذا ستتعلم في «الترقيم والترتيب وبث الشرائح»؟

إرجاع مجموعات نتائج مُرقّمة ومرتبة ومتدفقة بكفاءة لمجموعات البيانات الكبيرة وواجهات التمرير اللانهائي. تتمرن على Spring Boot 4 Complete Guide مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Spring Boot 4 Complete Guide؟

لا تُشترط خبرة سابقة. Spring Boot 4 Complete Guide على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «الترقيم والترتيب وبث الشرائح»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Spring Boot 4 Complete Guide هذا؟

نعم. كل درس في Spring Boot 4 Complete Guide يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. أساليب الاستعلام المشتقة وحل الكلمات المفتاحية
  2. استعلامات JPQL وSQL الأصلية باستخدام @Query
  3. المواصفات والتصفية الديناميكية القائمة على المعايير
  4. الترقيم والترتيب وبث الشرائح
← العودة إلى Spring Boot 4 Complete Guide