Spring Boot | Spring Data JPA | Pagination and Sorting
วันนี้เราจะมาดูตัวอย่างการใช้งาน 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}
ลองไปหัดใช้กันให้คล่องนะครับ