0Pricing
Java Academy · درس

التقسيم إلى صفحات والفرز والإسقاطات

أعِد النتائج مقسّمة إلى صفحات باستخدام Pageable، وفرزها ديناميكيًا، واستخدم إسقاطات الواجهات لتقليل الأعمدة المُجلَبة

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

لماذا نستخدم الترقيم بالصفحات؟

يؤدي إرجاع آلاف الصفوف في استعلام واحد إلى هدر الذاكرة وعرض النطاق الترددي. يحمّل الترقيم بالصفحات صفحة واحدة في كل مرة. يوفّر Spring Data الترقيم بالصفحات من خلال التجريد Pageable.

Pageable وPageRequest

أنشئوا كائن Pageable باستخدام PageRequest.of(page, size). ومرّروه إلى أي أسلوب مستودع يُرجع Page<T>.

Pageable pageable = PageRequest.of(0, 20); // page 0, 20 per page
Page<User> page = userRepo.findAll(pageable);
System.out.println("Total: " + page.getTotalElements());
System.out.println("Pages: " + page.getTotalPages());
System.out.println("Content: " + page.getContent().size());

الفرز باستخدام PageRequest

أضيفوا الفرز إلى PageRequest باستخدام كائن Sort. ويمكن ربط عدة حقول باستخدام Sort.by().and().

Pageable sorted = PageRequest.of(0, 20,
    Sort.by(Sort.Direction.DESC, "createdAt")
        .and(Sort.by("name")));
Page<User> page = userRepo.findAll(sorted);

Slice مقابل Page

ينفّذ Page<T> استعلام COUNT لحساب إجمالي الصفحات. أما Slice<T> فلا يفعل ذلك، بل يعرف فقط ما إذا كانت هناك صفحة تالية. استخدموا Slice للتمرير اللانهائي عندما لا تكون هناك حاجة إلى العدد الإجمالي.

public interface UserRepository extends JpaRepository<User, Long> {
    Slice<User> findByStatus(UserStatus status, Pageable pageable);
}

التكامل مع Spring MVC

يحل Spring MVC كائن Pageable تلقائيًا من معاملات الاستعلام: ?page=0&size=20&sort=name,asc. ولا حاجة إلى تحليل يدوي.

@GetMapping("/users")
public Page<UserDto> list(Pageable pageable) {
    return userRepo.findAll(pageable).map(UserDto::from);
}
// GET /users?page=0&size=10&sort=name,asc

الإسقاطات المعتمدة على الواجهات

عرّفوا واجهة تحتوي على أساليب getter تطابق أسماء حقول الكيان. يعيد Spring Data تلك الأعمدة فقط، مما يقلل نقل البيانات ويحجب التفاصيل الداخلية.

public interface UserSummary {
    Long getId();
    String getName();
    String getEmail();
}
List<UserSummary> findByStatus(UserStatus status);

الإسقاطات المعتمدة على الفئات (DTO)

استخدموا سجل DTO أو فئة DTO تحتوي على مُنشئ. ينشئ Spring Data تعبير مُنشئ JPQL للأعمدة المحددة.

public record UserDto(Long id, String name, String email) {}

@Query("SELECT new com.example.UserDto(u.id, u.name, u.email) FROM User u WHERE u.status = :s")
List<UserDto> findSummaries(@Param("s") UserStatus status);

الإسقاطات الديناميكية

مرّروا نوع الإسقاط باعتباره معلمة عامة إلى أسلوب المستودع لاختيار الإسقاط عند موضع الاستدعاء.

public interface UserRepository extends JpaRepository<User, Long> {
    <T> List<T> findByStatus(UserStatus status, Class<T> type);
}
// Usage:
List<UserSummary> summaries = repo.findByStatus(ACTIVE, UserSummary.class);
List<User>        full      = repo.findByStatus(ACTIVE, User.class);

الترقيم بالصفحات باستخدام المفتاح (Offset مقابل Keyset)

يتدهور أداء الترقيم بالإزاحة (OFFSET N) عند الإزاحات الكبيرة. أما الترقيم باستخدام المفتاح (باستخدام WHERE id > lastSeenId) فتعقيده O(1) بغض النظر عن عمق الصفحة، ولذلك فهو أفضل للصفحات العميقة.

// Keyset pagination:
@Query("SELECT u FROM User u WHERE u.id > :lastId ORDER BY u.id LIMIT :size")
List<User> findPage(@Param("lastId") Long lastId, @Param("size") int size);

تحسين حساب العدد الإجمالي

استخدموا countQuery في @Query لتوفير استعلام محسّن لحساب العدد، وتجنب عمليات JOIN المكلفة في الاستعلام الرئيسي.

@Query(value = "SELECT u FROM User u JOIN FETCH u.orders WHERE u.status = :s",
       countQuery = "SELECT COUNT(u) FROM User u WHERE u.status = :s")
Page<User> findActiveWithOrders(@Param("s") UserStatus s, Pageable p);

إرجاع ResponseEntity مع بيانات الصفحة الوصفية

ضمّنوا بيانات الصفحة الوصفية (totalElements وtotalPages وnumber وsize) في استجابة REST حتى تعرف العملاء كيفية التنقل بين الصفحات.

@GetMapping("/users")
public ResponseEntity<Map<String, Object>> list(Pageable p) {
    Page<UserDto> page = repo.findAll(p).map(UserDto::from);
    return ResponseEntity.ok(Map.of(
        "content", page.getContent(),
        "totalElements", page.getTotalElements(),
        "totalPages", page.getTotalPages()
    ));
}

تحقق سريع

ما الفرق الأساسي بين Page وSlice؟

مراجعة

استخدموا Pageable+PageRequest.of() للترقيم بالإزاحة. يحل Spring MVC كائن Pageable من معاملات الاستعلام. استخدموا Slice لتجنب COUNT. واستخدموا الإسقاطات المعتمدة على الواجهات أو DTO لتقليل الأعمدة التي يتم جلبها. استخدموا الترقيم باستخدام المفتاح عند التعامل مع الإزاحات الكبيرة.

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

هل درس «التقسيم إلى صفحات والفرز والإسقاطات» مجاني؟

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

ماذا ستتعلم في «التقسيم إلى صفحات والفرز والإسقاطات»؟

أعِد النتائج مقسّمة إلى صفحات باستخدام Pageable، وفرزها ديناميكيًا، واستخدم إسقاطات الواجهات لتقليل الأعمدة المُجلَبة تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟

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

كم من الوقت يستغرق درس «التقسيم إلى صفحات والفرز والإسقاطات»؟

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

هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟

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

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

  1. ربط الكيانات باستخدام تعليقات JPA
  2. مستودعات Spring Data وطرائق الاستعلام
  3. علاقات واحد-إلى-متعدد ومتعدد-إلى-متعدد
  4. التقسيم إلى صفحات والفرز والإسقاطات
← العودة إلى Java Academy