ตัวอย่างการเขียน Spring-boot WebFlux Pagination
pom.xml
...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.3.Final</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.5</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
...
spring-boot-starter-webflux
ใช้สำหรับเขียน webfluxspring-boot-starter-data-jpa
ไว้สำหรับเขียนคำสั่ง query, method queryhibernate-core
สำหรับทำ ORM (Object Relational Mapping) ไว้เขียนพวก entity class สำหรับ mapping java class ไปยัง database table รวมถึงการ mapping พวก relation ต่าง ๆ ของ table เช่น One to One, One to Many, Many to Manypostgresql
เป็น postgresql database driverHikariCP
เป็นตัวจัดการ database connection pool https://github.com/brettwooldridge/HikariCPlombok
เป็น annotation code generator สามารถ generate code at compile time ได้ ทำให้เราไม่ต้องเขียน code บางส่วนเอง เช่น getter setter method ตัว lombox จะทำให้
@SpringBootApplication
@ComponentScan(basePackages = {"com.pamarin"})
public class AppStarter {
public static void main(String[] args) {
SpringApplication.run(AppStarter.class, args);
}
}
@Data
@Entity
@Table(name = User.TABLE_NAME)
public class User implements Serializable {
public static final String TABLE_NAME = "user";
@Id
private String id;
@Column(name = "username", nullable = false, unique = true)
private String username;
@Column(name = "password", nullable = false)
private String password;
}
@Data
เป็น annotation ของ lombox เอาไว้ generate code เช่น getter/setter method, hashcode + equals ให้@Entity
เป็น annotation ที่เอาไว้ระบุว่า class นี้เป็น entity class@Table
เป็น annotation ที่เอาไว้ระบุว่าให้ class นี้ map ไปที่ database table ใด@Id
เป็น annotation ที่เอาไว้ระบุว่าจะให้ attribute ใดเป็น primary key@Column
เป็นการใช้ระบุข้อมูล column
public interface UserRepository extends JpaRepository<User, String>{
}
@RestController
public class UserController {
private final UserRepository userRepository;
@Autowired
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping({"", "/"})
public Mono<Page<User>> home(
@RequestParam(name = "page", defaultValue = "0") int page,
@RequestParam(name = "size", defaultValue = "10") int size
) {
return findAll(page, size);
}
@GetMapping("/users")
public Mono<Page<User>> findAll(
@RequestParam(name = "page", defaultValue = "0") int page,
@RequestParam(name = "size", defaultValue = "10") int size
) {
return Mono.just(userRepository.findAll(PageRequest.of(page, size)));
}
@GetMapping("/users/{id}")
public Mono<User> findById(@PathVariable("id") String id) {
return Mono.justOrEmpty(userRepository.findById(id))
.switchIfEmpty(Mono.error(new NotFoundException("Not found user of id " + id)));
}
@PostMapping("/users")
public Mono<User> save(@RequestBody User user) {
return Mono.just(userRepository.save(user));
}
@DeleteMapping("/users")
public Mono<Void> deleteAll() {
userRepository.deleteAll();
return Mono.empty();
}
@DeleteMapping("/users/{id}")
public Mono<Void> deleteById(@PathVariable("id") String id) {
userRepository.deleteById(id);
return Mono.empty();
}
}
Method ไหนที่ต้องการทำ page จะมีการกำหนด
@RequestParam(name = "page", defaultValue = "0") int page
และ@RequestParam(name = "size", defaultValue = "10") int size
เพื่อรับ query string page
และ size
จาก url เช่น http://localhost:8080?page=0&size=10
แล้วแปลงเป็น PageRequest.of(page, size)
ส่งเข้าไปเป็น parameter ของ userRepository.findAll(Pagable pagable)
เพื่อให้ spring-data นำไปแปลงเป็น sql query ต่อไป
@RequestParam
ถ้าเราไม่ส่ง query string นั้นมาทาง url มันจะใช้ค่า default ที่เรากำหนดไว้ใน defaultValue
#------------------------------------ JPA --------------------------------------
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.cache.use_second_level_cache=false
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.use-new-id-generator-mappings=true
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.proc.param_null_passing=true
spring.jpa.properties.hibernate.default_schema=*****
#------------------------------------ Hikari -----------------------------------
spring.datasource.hikari.minimumIdle=1
spring.datasource.hikari.maximumPoolSize=10
spring.datasource.hikari.idleTimeout=30000
spring.datasource.hikari.connectionTestQuery=SELECT 1 FROM DUAL
spring.datasource.hikari.validationTimeout=3000
#------------------------------------ Postgresql -------------------------------
spring.datasource.url=jdbc:postgresql:*****
spring.datasource.username=*****
spring.datasource.password=*****
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.platform=postgres
spring.datasource.type=org.postgresql.ds.PGSimpleDataSource
cd ไปที่ root ของ project จากนั้น
$ mvn clean install
$ mvn spring-boot:run \
-Dserver.port=8080 \
-Dspring.datasource.url=jdbc:postgresql://<HOST>:<PORT>/<DATABASE_NAME>?sslmode=require \
-Dspring.datasource.username=<DATABASE_USERNAME> \
-Dspring.datasource.password=<DATABASE_PASSWORD> \
-Dspring.jpa.properties.hibernate.default_schema=<DATABASE_SCHEMA>
ให้เปลี่ยน ค่า <>
เป็นของตัวเองน่ะครับ
- HOST คือ ip หรือ domain name ของ database server
- PORT คือ port ที่ใช้
- DATABASE_NAME คือ ชื่อ database
- DATABASE_USERNAME คือ ชื่อ username ที่ login เข้าใช้งาน database
- DATABASE_PASSWORD คือ รหัสผ่านที่คู่กับ username ที่ใช้
- DATABASE_SCHEMA คือ database schema ที่่ใช้
เปิด browser แล้วเข้า http://localhost:8080?page=0&size=10
หน้าตา page ที่ return มาจะประมาณนี้
{
"content": [
{
"id": "5cff55864ca1bc12305164ba",
"username": "[email protected]",
"password": "..."
},
{
"id": "5d09ffd14c3bda735d8ea555",
"username": "[email protected]",
"password": "..."
}
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"offset": 0,
"pageSize": 10,
"pageNumber": 0,
"unpaged": false,
"paged": true
},
"last": true,
"totalPages": 1,
"totalElements": 3,
"size": 10,
"number": 0,
"first": true,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"numberOfElements": 3,
"empty": false
}