Spring Boot | Spring Data JPA | Pagination and Sorting

Chiwa Kantawong (Pea)
6 min readAug 26, 2021

วันนี้เราจะมาดูตัวอย่างการใช้งาน Sprng JPA ในการทำ Pagination และ Sorting กันแบบง่ายๆ นะครับ เราจะใช้ Inmemory Database กันเพื่อที่ว่าจะได้ง่ายต่อการทำตัวอย่างนะครับ เช่นเคยเราจะใช้ Spring Boot with Maven กันนะครับ

เปิด project ด้วย Intellij จะได้ pom ไฟล์ ดังนี้

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zencode.jpa</groupId>
<artifactId>jpa-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>jpa-example</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>

เพิ่ม application.properties ครับ

spring.user.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:customerDB;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=

spring.jpa.show-sql=true
spring.h2.console.enabled=true
spring.h2.console.path=/h2
spring.jpa.hibernate.ddl-auto=create
security.headers.frame=false

ตอไปสร้าง Entity class ของ Customer กันครับ

package com.zencode.jpa.jpaexample.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Data
@Entity
@Table(name = "customers")
@NoArgsConstructor
@AllArgsConstructor
public class Customer {

@Id
@GeneratedValue
private int id;
private String name;
private int salary;
}

ต่อไปสร้าง CustomerRepository

package com.zencode.jpa.jpaexample.repository;

import com.zencode.jpa.jpaexample.entity.Customer;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CustomerRepository extends JpaRepository<Customer, Integer> {
}

สร้าง CustomerService

package com.zencode.jpa.jpaexample.service;

import com.zencode.jpa.jpaexample.entity.Customer;
import com.zencode.jpa.jpaexample.repository.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Service
public class CustomerService {

@Autowired
private CustomerRepository customerRepository;

@PostConstruct
public void InitData() {
List<Customer> customerList = IntStream.range(1, 300)
.mapToObj(i -> new Customer(i,"Customer " + i, new Random().nextInt(10000)))
.collect(Collectors.toList());
customerRepository.saveAll(customerList);
}

public List<Customer> findAllCustomers() {
return customerRepository.findAll();
}
}

เราจะ initial data ตอนที application ของเรา start นะครับ

ลองเช้า H2 Console กันครับ

ว้าว ได้แล้ว ต่อไปสร้าง Controller

package com.zencode.jpa.jpaexample.controller;

import com.zencode.jpa.jpaexample.entity.Customer;
import com.zencode.jpa.jpaexample.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class CustomerController {

@Autowired
private CustomerService customerService;

@GetMapping("/customers")
public List<Customer> getAllCustomer() {
return customerService.findAllCustomers();
}
}

ลองเข้า http://localhost:8080/customes

ต่อไปเราจะมา Sort กัน เพิ่มเข้าไปครับ

package com.zencode.jpa.jpaexample.service;

import com.zencode.jpa.jpaexample.entity.Customer;
import com.zencode.jpa.jpaexample.repository.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Service
public class CustomerService {

@Autowired
private CustomerRepository customerRepository;

@PostConstruct
public void InitData() {
List<Customer> customerList = IntStream.range(1, 300)
.mapToObj(i -> new Customer(i,"Customer " + i, new Random().nextInt(10000)))
.collect(Collectors.toList());
customerRepository.saveAll(customerList);
}

public List<Customer> findAllCustomers() {
return customerRepository.findAll();
}

public List<Customer> findAllCustomersWithSorting(String field) {
return customerRepository.findAll(Sort.by(Sort.Direction.DESC, field));
}
}

จากโค้ด ก็คือเราจะเรียงจากมากไปหาน้อย ตาม field ที่ระบุไปใน Parameter

ต่อไปเรามาสร้าง Controller กันครับ

package com.zencode.jpa.jpaexample.controller;

import com.zencode.jpa.jpaexample.entity.Customer;
import com.zencode.jpa.jpaexample.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class CustomerController {

@Autowired
private CustomerService customerService;

@GetMapping("/customers")
public List<Customer> getAllCustomer() {
return customerService.findAllCustomers();
}
@GetMapping("/customers/sort_by/{field}")
public List<Customer> getAllCustomerSortedByField(@PathVariable String field) {
return customerService.findAllCustomersWithSorting(field);
}
}

ลองเรียกดูครับ

  • Sort by salary
  • Sort by name ดูครับ

ว้าว ง่ายเนอะ ลองคิดดูถ้าเราใช้ JDBC Template จะเกิดอะไรขึ้น เขียนโค้ดกันสนานเลยงานนี้ ใช่ไหมครับ

ต่อไปเราจะมาสร้าง Pagination กันครับ

package com.zencode.jpa.jpaexample.service;

import com.zencode.jpa.jpaexample.entity.Customer;
import com.zencode.jpa.jpaexample.repository.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Service
public class CustomerService {

@Autowired
private CustomerRepository customerRepository;

@PostConstruct
public void InitData() {
List<Customer> customerList = IntStream.range(1, 300)
.mapToObj(i -> new Customer(i,"Customer " + i, new Random().nextInt(10000)))
.collect(Collectors.toList());
customerRepository.saveAll(customerList);
}

public List<Customer> findAllCustomers() {
return customerRepository.findAll();
}

public List<Customer> findAllCustomersWithSorting(String field) {
return customerRepository.findAll(Sort.by(Sort.Direction.DESC, field));
}

public Page<Customer> findAllCustomersWithPagination(int offset, int pageSize) {
return customerRepository.findAll(PageRequest.of(offset, pageSize));
}
}

มาสร้าง Controller กัน

package com.zencode.jpa.jpaexample.controller;

import com.zencode.jpa.jpaexample.entity.Customer;
import com.zencode.jpa.jpaexample.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class CustomerController {

@Autowired
private CustomerService customerService;

@GetMapping("/customers")
public List<Customer> getAllCustomer() {
return customerService.findAllCustomers();
}

@GetMapping("/customers/sort_by/{field}")
public List<Customer> getAllCustomerSortedByField(@PathVariable String field) {
return customerService.findAllCustomersWithSorting(field);
}

@GetMapping("/customers/offset/{offset}/page-size/{pageSize}")
public Page<Customer> getAllCustomerPagination(@PathVariable int offset, @PathVariable int pageSize) {
return customerService.findAllCustomersWithPagination(offset, pageSize);
}
}

ลองเรียกใช้ดูครับ

Response จะมีหน้าตาดังนี้

{"content": [{"id": 1,"name": "Customer 1","salary": 6958},{"id": 2,"name": "Customer 2","salary": 5873},{"id": 3,"name": "Customer 3","salary": 8287},{"id": 4,"name": "Customer 4","salary": 8425},{"id": 5,"name": "Customer 5","salary": 874},{"id": 6,"name": "Customer 6","salary": 1095},{"id": 7,"name": "Customer 7","salary": 1071},{"id": 8,"name": "Customer 8","salary": 3576},{"id": 9,"name": "Customer 9","salary": 8252},{"id": 10,"name": "Customer 10","salary": 9430}],"pageable": {"sort": {"sorted": false,"unsorted": true,"empty": true},"pageNumber": 0,"pageSize": 10,"offset": 0,"unpaged": false,"paged": true},"totalPages": 30,"totalElements": 299,"last": false,"first": true,"numberOfElements": 10,"size": 10,"number": 0,"sort": {"sorted": false,"unsorted": true,"empty": true},"empty": false}

โอ้วววว ง่ายดาย จิม จิม

เราก็สามารถใช้ Pagination ร่วมกัน Sort ได้นะครับ เช่น

package com.zencode.jpa.jpaexample.service;

import com.zencode.jpa.jpaexample.entity.Customer;
import com.zencode.jpa.jpaexample.repository.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Service
public class CustomerService {

@Autowired
private CustomerRepository customerRepository;

........

public Page<Customer> findAllCustomersWithPaginationSortBySalary(int offset, int pageSize) {
return customerRepository.findAll(PageRequest.of(offset, pageSize, Sort.by("salary").descending()));
}
}

เราสามารถทำ and , or ในการ Sort ได้นะครับ เช่น

public Page<Customer> findAllCustomersWithPaginationSortBySalary(int offset, int pageSize) {
return customerRepository.findAll(PageRequest.of(offset, pageSize, Sort.by("salary").descending().and(Sort.by("name").ascending())));
}

Controller เพิ่มแบบนี้ครับ

package com.zencode.jpa.jpaexample.controller;

import com.zencode.jpa.jpaexample.entity.Customer;
import com.zencode.jpa.jpaexample.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class CustomerController {

@Autowired
private CustomerService customerService;

.......

@GetMapping("/sorted-salary-customers/offset/{offset}/page-size/{pageSize}")
public Page<Customer> getAllCustomersWithPaginationSortBySalary(@PathVariable int offset, @PathVariable int pageSize) {
return customerService.findAllCustomersWithPaginationSortBySalary(offset, pageSize);
}
}

ลองเรียกดูครับ

Response

{"content": [{"id": 13,"name": "Customer 13","salary": 9978},{"id": 152,"name": "Customer 152","salary": 9899},{"id": 205,"name": "Customer 205","salary": 9857},{"id": 155,"name": "Customer 155","salary": 9834},{"id": 167,"name": "Customer 167","salary": 9796},{"id": 29,"name": "Customer 29","salary": 9703},{"id": 75,"name": "Customer 75","salary": 9658},{"id": 56,"name": "Customer 56","salary": 9631},{"id": 231,"name": "Customer 231","salary": 9622},{"id": 194,"name": "Customer 194","salary": 9579}],"pageable": {"sort": {"unsorted": false,"sorted": true,"empty": false},"pageNumber": 0,"pageSize": 10,"offset": 0,"unpaged": false,"paged": true},"totalPages": 30,"totalElements": 299,"last": false,"numberOfElements": 10,"first": true,"sort": {"unsorted": false,"sorted": true,"empty": false},"size": 10,"number": 0,"empty": false}

ลองไปหัดใช้กันให้คล่องนะครับ

--

--