وقتی در برنامه نقاط پایانی (endpoint) میسازیم که مجموعهای از موجودیتها (entities) را برمیگردانند، خیلی مهم است که صفحهبندی را پیادهسازی کنیم. در غیر این صورت ممکن است با مشکلاتی مثل طولانی شدن زمان پاسخ، مصرف بیش از حد حافظه و حتی بعضی حملات (مثل حملات انکار سرویس با درخواستهای خیلی بزرگ) روبهرو شویم.
در این مطلب قرار است یاد بگیریم چطور میتوانیم دو رویکرد مختلف — Paging و Skip/Top — را در یک پروژهی اسپرینگ بوت با استفاده از JPA و JOOQ پیادهسازی کنیم.
مقایسهی Paging و Skip/Top
در روش Paging، نقطه پایانی (endpoint) طوری طراحی میشود که دو پارامتر دریافت کند: شمارهی صفحه (page number) و اندازهی صفحه (page size). این دو پارامتر مشخص میکنند کدام بخش از دادهها باید برگردانده شوند.
معمولاً در پاسخهای 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 @GetMapping("/jpa/withPage") @GetMapping("/jpa/withSkipAndTop") @GetMapping("/jooq/withPage") @GetMapping("/jooq/withSkipAndTop") |
اینجا دو کلاس سرویس داریم: یکی برای JPA و دیگری برای JOOQ.
کاربران جاواپرو که علاقهمند به یادگیری Spring Boot هستند، در دورههای زیر ثبتنام کردهاند:
Paging با JPA
خود JPA بهصورت داخلی از صفحهبندی پشتیبانی میکند، بنابراین پیادهسازی آن خیلی ساده است. کافی است در مخزن (Repository) متدی تعریف کنید که یک پارامتر از نوع Pageable بگیرد و یک Page برگرداند.
در ادامه، کلاس سرویس متد مخزن را صدا میزند و از یک PageRequest که با شماره صفحه (page number) و اندازه صفحه (page size) ساخته شده، استفاده میکند.
@Repository @Service public Page<Product> findByPage(int page, int size) { } |
پاسخ به این شکل خواهد بود:
{ |
Skip و Top با JPA
خود JPA بهصورت پیشفرض از skip و top پشتیبانی نمیکند، بنابراین برای رسیدن به این قابلیت باید از راهکارهای جایگزین استفاده کنیم. یکی از گزینهها این است که شماره صفحه و اندازه صفحه را بر اساس مقادیر skip و top محاسبه کنیم؛ هرچند این روش همیشه بهطور قابل اعتماد جواب نمیدهد. راهحل بهتر این است که یک Pageable سفارشی پیادهسازی کنیم و با بازنویسی متدهای لازم، رفتار درست را ایجاد کنیم.
@RequiredArgsConstructor(staticName = "of") private final int skip; private final Sort sort = Sort.unsorted(); @Override @Override @Override @Override @Override @Override @Override @Override @Override |
با این کار، کلاس سرویس تا حد امکان ساده باقی میماند — فقط یک نمونه از PageableWithSkipAndTop میسازد و یک لیست از Products برمیگرداند.
@Service public List<Product> findWithSkipAndTop(int skip, int top) { |
JOOQ با Skip و Top
از طرف دیگر، خود JOOQ بهصورت داخلی از skip و top پشتیبانی میکند. فقط کافی است این عبارتها را مستقیماً داخل کوئری بهکار ببرید، همانطور که در پیادهسازی مخزن (Repository) نشان داده شده است.
@Repository public List<ProductRecord> findWithSkipAndTop(int skip, int top) { |
مخزن (Repository) از یک کلاس ProductTable استفاده میکند که تعریف جدول در JOOQ را در خود دارد و همچنین یک ProductRecord DTO برای نگهداری نتایج کوئری.
کلاس سرویس هم خیلی ساده است:
@Service public List<ProductRecord> findWithSkipAndTop(int skip, int top) { |
JOOQ با Pagination
اینجا کمی قضیه پیچیدهتر میشود. از آنجایی که JOOQ بهصورت پیشفرض مثل JPA از صفحهبندی پشتیبانی نمیکند، باید از limit و OFFSET برای گرفتن رکوردهای درست استفاده کنید. علاوه بر این، نیاز است دو کوئری اجرا شود: یکی برای گرفتن تعداد کل رکوردها و دیگری برای دریافت صفحهی واقعی نتایج.
@Repository public Page<ProductRecord> findWithPageable(int page, int size) { List<ProductRecord> records = int total = dslContext.fetchCount(DSL.select().from(ProductTable.PRODUCT)); return new PageImpl<>(records, pageable, total); } |
خلاصه
برای پیادهسازی صفحهبندی میتوانید از JPA یا JOOQ استفاده کنید، اما چند تفاوت مهم وجود دارد:
اگر قصد دارید از صفحهبندی مبتنی بر شماره صفحه استفاده کنید، JPA گزینهی بهتری است.
اگر نیاز به پیادهسازی skip و top دارید، JOOQ مناسبتر است.
بستن *نام و نام خانوادگی * پست الکترونیک * متن پیام |
دوره های آموزشی برنامه نویسی
انجام پروژه های برنامه نویسی
تدریس خصوصی برنامه نویسی
بیش از 7 سال از فعالیت جاواپرو میگذرد
جاواپرو دارای مجوز نشر دیجیتال از وزارت فرهنگ و ارشاد اسلامی است
جهت ارتباط مستقیم با جاواپرو در واتساپ و تلگرام :
09301904690