Database Pagination কী?

Pagination হলো বড় ডেটাসেটকে ছোট ছোট ভাগে ভাগ করে দেখানোর পদ্ধতি। একসাথে লক্ষ লক্ষ রেকর্ড লোড না করে Page ভিত্তিক করে ডেটা আনা হয়। এটি Performance ও User Experience উভয়ই উন্নত করে।


কেন Pagination দরকার?

-- ❌ খারাপ উদাহরণ: সব ডেটা একসাথে
SELECT * FROM products;  -- ১০ লক্ষ রেকর্ড!

-- ✅ ভালো উদাহরণ: Page ভিত্তিক
SELECT * FROM products LIMIT 20 OFFSET 0;   -- Page 1
SELECT * FROM products LIMIT 20 OFFSET 20;  -- Page 2
SELECT * FROM products LIMIT 20 OFFSET 40;  -- Page 3

সুবিধা:

  • Memory কম ব্যবহার হয়
  • Database Load কম
  • Network Traffic কম
  • User দ্রুত ফলাফল পায়

SQL Pagination পদ্ধতি

LIMIT/OFFSET (সাধারণ পদ্ধতি)

-- Page 1: প্রথম ২০টি রেকর্ড
SELECT id, name, price FROM products
ORDER BY id
LIMIT 20 OFFSET 0;

-- Page 2: পরের ২০টি রেকর্ড
SELECT id, name, price FROM products
ORDER BY id
LIMIT 20 OFFSET 20;

-- Page N: OFFSET = (page - 1) * pageSize
SELECT id, name, price FROM products
ORDER BY id
LIMIT 20 OFFSET 100;  -- Page 6

Keyset/Cursor Pagination (আরও দক্ষ)

-- প্রথম Page
SELECT id, name, price FROM products
ORDER BY id
LIMIT 20;
-- শেষ id = 20

-- পরের Page (last_id = 20 ব্যবহার করে)
SELECT id, name, price FROM products
WHERE id > 20  -- Index ব্যবহার করে, OFFSET নয়
ORDER BY id
LIMIT 20;

Keyset Pagination কেন ভালো?

  • OFFSET বড় হলে ধীর হয় (OFFSET 100000 = ১ লক্ষ Row skip করতে হয়)
  • Keyset Pagination সবসময় Index ব্যবহার করে — দ্রুত

Spring Boot JPA-তে Pagination

Repository

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface ProductRepository extends JpaRepository<Product, Long> {

    // Built-in Pagination support
    Page<Product> findAll(Pageable pageable);

    // Filter সহ Pagination
    Page<Product> findByCategory(String category, Pageable pageable);

    // Price range সহ Pagination
    Page<Product> findByPriceBetween(double minPrice, double maxPrice, Pageable pageable);
}

Service

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    // Basic Pagination
    public Page<Product> getProducts(int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        return productRepository.findAll(pageable);
    }

    // Sorting সহ Pagination
    public Page<Product> getProductsSorted(int page, int size, String sortBy) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy).ascending());
        return productRepository.findAll(pageable);
    }

    // Multi-field Sorting
    public Page<Product> getProductsMultiSort(int page, int size) {
        Sort sort = Sort.by(
            Sort.Order.asc("category"),
            Sort.Order.desc("price")
        );
        Pageable pageable = PageRequest.of(page, size, sort);
        return productRepository.findAll(pageable);
    }

    // Category Filter সহ Pagination
    public Page<Product> getByCategory(String category, int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        return productRepository.findByCategory(category, pageable);
    }
}

Controller

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @Autowired
    private ProductService productService;

    @GetMapping
    public ResponseEntity<Map<String, Object>> getProducts(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size,
        @RequestParam(defaultValue = "id") String sortBy
    ) {
        Page<Product> productPage = productService.getProductsSorted(page, size, sortBy);

        Map<String, Object> response = new HashMap<>();
        response.put("products", productPage.getContent());
        response.put("currentPage", productPage.getNumber());
        response.put("totalItems", productPage.getTotalElements());
        response.put("totalPages", productPage.getTotalPages());
        response.put("isFirst", productPage.isFirst());
        response.put("isLast", productPage.isLast());

        return ResponseEntity.ok(response);
    }
}

Page Object-এর উপকারী মেথড

Page<Product> page = productRepository.findAll(pageable);

page.getContent();          // বর্তমান Page-এর ডেটা List
page.getNumber();           // বর্তমান Page নম্বর (0-based)
page.getSize();             // Page Size
page.getTotalElements();    // মোট রেকর্ড সংখ্যা
page.getTotalPages();       // মোট Page সংখ্যা
page.isFirst();             // প্রথম Page কিনা
page.isLast();              // শেষ Page কিনা
page.hasNext();             // পরের Page আছে কিনা
page.hasPrevious();         // আগের Page আছে কিনা

Custom Pagination Response DTO

public class PageResponse<T> {
    private List<T> content;
    private int currentPage;
    private long totalItems;
    private int totalPages;
    private boolean isFirst;
    private boolean isLast;
    private boolean hasNext;
    private boolean hasPrevious;

    public static <T> PageResponse<T> of(Page<T> page) {
        PageResponse<T> response = new PageResponse<>();
        response.setContent(page.getContent());
        response.setCurrentPage(page.getNumber());
        response.setTotalItems(page.getTotalElements());
        response.setTotalPages(page.getTotalPages());
        response.setFirst(page.isFirst());
        response.setLast(page.isLast());
        response.setHasNext(page.hasNext());
        response.setHasPrevious(page.hasPrevious());
        return response;
    }
}

Keyset Pagination in Spring JPA

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {

    // Cursor-based Pagination: last seen id-এর পরের records
    @Query("SELECT p FROM Product p WHERE p.id > :lastId ORDER BY p.id ASC")
    List<Product> findNextPage(@Param("lastId") Long lastId, Pageable pageable);
}

@Service
public class ProductService {

    public List<Product> getNextPage(Long lastId, int size) {
        Pageable pageable = PageRequest.of(0, size);
        return productRepository.findNextPage(lastId, pageable);
    }
}

সংক্ষেপে

বিষয় বিবরণ
Pagination বড় ডেটা Page ভিত্তিক লোড করা
LIMIT/OFFSET সহজ কিন্তু বড় OFFSET-এ ধীর
Keyset Pagination সবসময় দ্রুত, Index ব্যবহার করে
PageRequest.of() Spring-এ Pageable তৈরির পদ্ধতি
Page<T> Pagination metadata সহ ডেটা রাখে
Slice<T> Metadata কম, শুধু isLast/isNext জানে

উপসংহার

Pagination যেকোনো Production অ্যাপ্লিকেশনে অপরিহার্য। Spring Data JPA-এর Pageable ইন্টারফেস ব্যবহার করে অত্যন্ত সহজে Pagination implement করা যায়। বড় টেবিলের জন্য Keyset Pagination ব্যবহার করুন — এটি OFFSET-based Pagination-এর চেয়ে অনেক দ্রুত।

Share