menusearch
javapro.ir

چطور قابلیت صفحه‌بندی (Paging) را در اسپرینگ بوت پیاده‌سازی کنیم

جستجو
دوشنبه ۲۴ شهریور ۱۴۰۴ | ۲۲:۳۴:۲۵
۱۴۰۴/۶/۲۴ دوشنبه
(1)
(0)
چطور قابلیت صفحه‌بندی (Paging) را در اسپرینگ بوت پیاده‌سازی کنیم
چطور قابلیت صفحه‌بندی (Paging) را در اسپرینگ بوت پیاده‌سازی کنیم

چطور قابلیت صفحه‌بندی (Paging) را در اسپرینگ بوت پیاده‌سازی کنیم

وقتی در برنامه نقاط پایانی (endpoint) می‌سازیم که مجموعه‌ای از موجودیت‌ها (entities) را برمی‌گردانند، خیلی مهم است که صفحه‌بندی را پیاده‌سازی کنیم. در غیر این صورت ممکن است با مشکلاتی مثل طولانی شدن زمان پاسخ، مصرف بیش از حد حافظه و حتی بعضی حملات (مثل حملات انکار سرویس با درخواست‌های خیلی بزرگ) روبه‌رو شویم.

در این مطلب قرار است یاد بگیریم چطور می‌توانیم دو رویکرد مختلف — Paging و Skip/Top — را در یک پروژه‌ی اسپرینگ بوت با استفاده از JPA و JOOQ پیاده‌سازی کنیم.

 

صفحه‌بندی (Paging) را در اسپرینگ بوت


مقایسه‌ی Paging و Skip/Top

در روش Paging، نقطه پایانی (endpoint) طوری طراحی می‌شود که دو پارامتر دریافت کند: شماره‌ی صفحه (page number) و اندازه‌ی صفحه (page size). این دو پارامتر مشخص می‌کنند کدام بخش از داده‌ها باید برگردانده شوند.

 

این مقاله جذاب و کاربردی در زمینه Spring Boot را از دست ندهید

 


معمولاً در پاسخ‌های Paging علاوه بر داده‌های انتخاب‌شده، یک‌سری اطلاعات کمکی (metadata) هم وجود دارد؛ مثل اندازه‌ی صفحه، تعداد کل رکوردها و شماره‌ی صفحه‌ی فعلی.

از طرف دیگر، در روش Skip/Top دو پارامتر وجود دارد که نتیجه‌ی مشابهی به دست می‌دهند. در این حالت شما مشخص می‌کنید چند رکورد باید رد شوند (skip) و چند رکورد باید برگردند (top). به‌عنوان مثال، وقتی می‌گوییم skip 10 و top 20 یعنی ۱۰ رکورد اول کنار گذاشته شود و ۲۰ رکورد بعدی بازگردانده شود. این روش معمولاً فقط لیست داده‌ها را برمی‌گرداند و خبری از اطلاعات تکمیلی (metadata) نیست.

Controller

قبل از اینکه سراغ کد مخزن (Repository) برویم ـ جایی که اصل کار انجام می‌شود ـ بیایید نگاهی به کلاس Controller بیندازیم. این کلاس چهار endpoint دارد:

JPA همراه با صفحه‌بندی (pagination)

JPA همراه با skip و top

JOOQ همراه با صفحه‌بندی (pagination)

JOOQ همراه با skip و top

 

@RestController
@RequestMapping("/products")
@RequiredArgsConstructor
public class ProductController {
  private final ProductService productService;
  private final ProductServiceJooq productServiceJooq;

  @GetMapping("/jpa/withPage")
  @ResponseStatus(HttpStatus.OK)
  public Page<Product> findAll(
      @RequestParam(name = "page") int page, @RequestParam(name = "pageSize") int size) {
    return productService.findByPage(page, size);
  }

  @GetMapping("/jpa/withSkipAndTop")
  @ResponseStatus(HttpStatus.OK)
  public Iterable<Product> findWithSkipAndTop(
      @RequestParam(name = "skip") int skip, @RequestParam(name = "top") int top) {
    return productService.findWithSkipAndTop(skip, top);
  }

  @GetMapping("/jooq/withPage")
  @ResponseStatus(HttpStatus.OK)
  public Page<ProductRecord> findAllJooq(
      @RequestParam(name = "page") int page, @RequestParam(name = "pageSize") int size) {
    return productServiceJooq.findWithPageable(page, size);
  }

  @GetMapping("/jooq/withSkipAndTop")
  @ResponseStatus(HttpStatus.OK)
  public Iterable<ProductRecord> findWithSkipAndTopJooq(
      @RequestParam(name = "skip") int skip, @RequestParam(name = "top") int top) {
    return productServiceJooq.findWithSkipAndTop(skip, top);
  }
}

 


اینجا دو کلاس سرویس داریم: یکی برای JPA و دیگری برای JOOQ.

 

 

 

کاربران جاواپرو که علاقه‌مند به یادگیری Spring Boot هستند، در دوره‌های زیر ثبت‌نام کرده‌اند:

 

 

 

 

Paging با JPA

خود JPA به‌صورت داخلی از صفحه‌بندی پشتیبانی می‌کند، بنابراین پیاده‌سازی آن خیلی ساده است. کافی است در مخزن (Repository) متدی تعریف کنید که یک پارامتر از نوع Pageable بگیرد و یک Page برگرداند.

در ادامه، کلاس سرویس متد مخزن را صدا می‌زند و از یک PageRequest که با شماره صفحه (page number) و اندازه صفحه (page size) ساخته شده، استفاده می‌کند.

 

@Repository
public interface ProductRepository extends CrudRepository<Product, Long> {
  Page<Product> findAll(Pageable pageable);
}

@Service
@RequiredArgsConstructor
public class ProductService {
  private final ProductRepository productRepository;

  public Page<Product> findByPage(int page, int size) {
    return productRepository.findAll(PageRequest.of(page, size));
  }

}

 


پاسخ به این شکل خواهد بود:

 

{
    "content": [
        {
            "id": 26,
            "name": "Product 1",
            "description": "Description for Product 1",
            "price": 10.99,
            "image": "image1.jpg",
            ...
        },
        {
            "id": 27,
            "name": "Product 2",
            "description": "Description for Product 2",
            "price": 15.49,
            "image": "image2.jpg",
            ...
        },
        ...
    ],
    "pageable": {
        "pageNumber": 0,
        "pageSize": 10,
        "sort": {
            "empty": true,
            "sorted": false,
            "unsorted": true
        },
        "offset": 0,
        "unpaged": false,
        "paged": true
    },
    "last": false,
    "totalPages": 3,
    "totalElements": 25,
    "first": true,
    "size": 10,
    "number": 0,
    "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
    },
    "numberOfElements": 10,
    "empty": false
}

 

 

Skip و Top با JPA

خود JPA به‌صورت پیش‌فرض از skip و top پشتیبانی نمی‌کند، بنابراین برای رسیدن به این قابلیت باید از راهکارهای جایگزین استفاده کنیم. یکی از گزینه‌ها این است که شماره صفحه و اندازه صفحه را بر اساس مقادیر skip و top محاسبه کنیم؛ هرچند این روش همیشه به‌طور قابل اعتماد جواب نمی‌دهد. راه‌حل بهتر این است که یک Pageable سفارشی پیاده‌سازی کنیم و با بازنویسی متدهای لازم، رفتار درست را ایجاد کنیم.

 

@RequiredArgsConstructor(staticName = "of")
public class PageableWithSkipAndTop implements Pageable {

  private final int skip;
  private final int top;

  private final Sort sort = Sort.unsorted();

  @Override
  public int getPageNumber() {
    return 0;
  }

  @Override
  public int getPageSize() {
    return top;
  }

  @Override
  public long getOffset() {
    return skip;
  }

  @Override
  public Sort getSort() {
    return sort;
  }

  @Override
  public Pageable next() {
    return this;
  }

  @Override
  public Pageable previousOrFirst() {
    return this;
  }

  @Override
  public Pageable first() {
    return this;
  }

  @Override
  public Pageable withPage(int pageNumber) {
    return this;
  }

  @Override
  public boolean hasPrevious() {
    return false;
  }
}

 

با این کار، کلاس سرویس تا حد امکان ساده باقی می‌ماند — فقط یک نمونه از PageableWithSkipAndTop می‌سازد و یک لیست از Products برمی‌گرداند.

 

@Service
@RequiredArgsConstructor
public class ProductService {
  private final ProductRepository productRepository;

  public List<Product> findWithSkipAndTop(int skip, int top) {
    return productRepository.findAll(PageableWithSkipAndTop.of(skip, top)).getContent();
  }
}

 

JOOQ با Skip و Top

از طرف دیگر، خود JOOQ به‌صورت داخلی از skip و top پشتیبانی می‌کند. فقط کافی است این عبارت‌ها را مستقیماً داخل کوئری به‌کار ببرید، همان‌طور که در پیاده‌سازی مخزن (Repository) نشان داده شده است.

 

@Repository
@RequiredArgsConstructor
public class ProductRepositoryJooq {
  private final DSLContext dslContext;

  public List<ProductRecord> findWithSkipAndTop(int skip, int top) {
    return dslContext
 . select ()
 . from (ProductTable.PRODUCT)
        .offset(skip)
 . limit (top)
        .fetchinto(ProductRecord.class);
  }
}

 

مخزن (Repository) از یک کلاس ProductTable استفاده می‌کند که تعریف جدول در JOOQ را در خود دارد و همچنین یک ProductRecord DTO برای نگهداری نتایج کوئری.

کلاس سرویس هم خیلی ساده است:

 

@Service
@RequiredArgsConstructor
public class ProductServiceJooq {
  private final ProductRepositoryJooq productRepositoryJooq;

  public List<ProductRecord> findWithSkipAndTop(int skip, int top) {
    return productRepositoryJooq.findWithSkipAndTop(skip, top);
  }
}

 


JOOQ با Pagination

اینجا کمی قضیه پیچیده‌تر می‌شود. از آنجایی که JOOQ به‌صورت پیش‌فرض مثل JPA از صفحه‌بندی پشتیبانی نمی‌کند، باید از limit و OFFSET برای گرفتن رکوردهای درست استفاده کنید. علاوه بر این، نیاز است دو کوئری اجرا شود: یکی برای گرفتن تعداد کل رکوردها و دیگری برای دریافت صفحه‌ی واقعی نتایج.

 

@Repository
@RequiredArgsConstructor
public class ProductRepositoryJooq {
  private final DSLContext dslContext;

  public Page<ProductRecord> findWithPageable(int page, int size) {
    Pageable pageable = PageRequest.of(page, size);

    List<ProductRecord> records =
        dslContext
     . select from(ProductTable.PRODUCT)
     . limit (pageable.getPageSize())
            .offset((int) pageable.getOffset())
            .fetchinto(ProductRecord.class);

    int total = dslContext.fetchCount(DSL.select().from(ProductTable.PRODUCT));

    return new PageImpl<>(records, pageable, total);
  }

}

 

خلاصه

برای پیاده‌سازی صفحه‌بندی می‌توانید از JPA یا JOOQ استفاده کنید، اما چند تفاوت مهم وجود دارد:

اگر قصد دارید از صفحه‌بندی مبتنی بر شماره صفحه استفاده کنید، JPA گزینه‌ی بهتری است.

اگر نیاز به پیاده‌سازی skip و top دارید، JOOQ مناسب‌تر است.

 

 

منبع: نوشته شده توسط Renan Schmitt

نظرات کاربران
*نام و نام خانوادگی
* پست الکترونیک
* متن پیام

بستن
*نام و نام خانوادگی
* پست الکترونیک
* متن پیام

0 نظر
هدر سایت
جشنواره تخفیفات 60% پایان فصل جاواپرو | مشاهده لیست دوره ها [اینجا کلیک کنید]
دوره جامع نخبگان معماری میکروسرویس ها با Java و Spring Boot
دوره برنامه نویسی Spring Core
مشاهده سرفصل ها و ثبت نام در دوره Spring Boot جاواپرو  [کلیک کنید]
آموزش پروژه محور اسپرینگ بوت(Spring Boot)-سیستم دانشگاه
ثبت نام در دوره آموزش Spring security
دوره پرتاب | آموزش پیش نیازهای برنامه نویسی
دوره آموزش مبانی زبان برنامه نویسی جاوا
دوره آموزش مفاهیم پیشرفته زبان برنامه نویسی جاوا
مقدمه ای از زبان برنامه نویسی جاوا(java)
آموزش زبان برنامه نویسی جاوا
آموزش گرافیک در زبان برنامه نویسی جاوا
آموزش مدیریت چیدمان گرافیکی در زبان جاوا
آموزش ساخت بازی دوبعدی در زبان جاوا
Collection ها در زبان برنامه نویسی جاوا
آموزش پروژه محور ساخت برنامه مدیریت بانک با JavaFX
نمونه پروژه های رایگان زبان جاوا
آموزش دیتابیس در زبان برنامه نویسی جاوا
نمونه مثال پایه ای زبان برنامه نویسی جاوا
نمونه مثال String در زبان برنامه نویسی جاوا
آموزش جامع برنامه نویسی JavaFX
آموزش ساخت برنامه آزمون تستی در JavaFX
آموزش برنامه نویسی سوکت در جاوا
آموزش ساخت برنامه دفترچه تلفن با JavaFX
آموزش ساخت ربات ساده تلگرام با زبان جاوا
آموزش ساخت برنامه ماشین حساب با JavaFX
آموزش ساخت برنامه ساده مدیریت ایمیل ها با JavaFX
دوره آموزش Spring Boot
سفارش انجام پروژه زبان برنامه نویسی جاوا(JAVA)
سفارش انجام پروژه برنامه نویسی متلب(MATLAB) با قیمت منصفانه و تحویل به موقع
سفارش انجام پروژه زبان برنامه نویسی سی شارپ (#C)
سفارش انجام پروژه زبان برنامه نویسی سی(C)
سفارش انجام پروژه زبان برنامه نویسی پایتون(Python)
سفارش انجام پروژه زبان برنامه نویسی PHP (پی اچ پی)
سفارش انجام پروژه زبان برنامه نویسی اسمبلی(Assembly)
سفارش انجام پروژه زبان برنامه نویسی جاوا اسکریپت (Javascript)
سفارش انجام پروژه هوش مصنوعی
سفارش انجام پروژه طراحی الگوریتم
سفارش انجام پروژه ساختمان داده ها
سفارش انجام پروژه مهندسی نرم افزار
سفارش انجام پروژه شبکه های کامپیوتری
سفارش انجام پروژه پایگاه داده: دیتابیس (database)
 سفارش انجام پروژه سیستم عامل
سفارش انجام پروژه پاورپوینت(PowerPoint)
سفارش انجام پروژه اکسل (Excel)
سفارش انجام تحقیق و تهیه مقاله
سوالات متداول برنامه نویسی
جدیدترین مطالب
طراحی توسط سایت ساز خبری
گفتگو را شروع کنید
مشاوره ،کلاس خصوصی آنلاین،پشتیبانی دوره های آکادمی جاواپرو و سفارش پروژه