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}

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

--

--

Chiwa Kantawong (Pea)
Chiwa Kantawong (Pea)

Written by Chiwa Kantawong (Pea)

Software Development Expert at Central Tech

No responses yet