Skip to content

Latest commit

 

History

History

spring-boot-reactive-r2dbc-native-sql

spring-boot-reactive-r2dbc-native-sql

ตัวอย่างการใช้ Database Client เพื่อเขียน Native SQL Query ข้อมูลสำหรับ Spring-boot Reactive R2DBC (The Reactive Relational Database Connectivity)

  • R2DBC (The Reactive Relational Database Connectivity) เป็น Library/Dependency ฝั่งภาษา Java สำหรับการเขียน Code เพื่อเชื่อมต่อไปยัง Database แบบ Reactive (Non-Block I/O)
  • มี Spring-data รองรับ เพื่อให้สามารถเขียน CRUD และเขียน Query อื่น ๆ ได้ง่ายขึ้น
  • DatabaseClient เป็น Class/Component นึงของ R2DBC เพื่อใช้สำหรับ Query ข้อมูลจาก Database เองแบบ Manual

เว็บไซต์

Prerequisites

  • เตรียมฐานข้อมูล PostgreSQL ให้พร้อม
docker run -d -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=password postgres
  • สร้าง schema app
  • สร้าง table user ที่ schema app โดยใช้ SQL นี้
CREATE SCHEMA "app";
CREATE TABLE "app"."user" (
    "id" UUID NOT NULL,
    "username" varchar(50) NOT NULL,
    "first_name" varchar(50) NOT NULL,
    "last_name" varchar(50) NOT NULL,
    PRIMARY KEY ("id")
);

1. เพิ่ม Dependencies และ Plugins

pom.xml

...
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.1</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>

    <!-- Database ****************************************************** -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-r2dbc</artifactId>
    </dependency>

    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>r2dbc-postgresql</artifactId>
        <version>1.0.4.RELEASE</version>
    </dependency>
    <!-- Database ****************************************************** -->
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>        
                <execution>            
                    <id>build-info</id>            
                    <goals>                
                        <goal>build-info</goal>            
                    </goals>        
                    <configuration>                
                        <additionalProperties>                    
                            <java.version>${java.version}</java.version>                                   
                        </additionalProperties>            
                    </configuration>        
                </execution>    
            </executions>
        </plugin>
    </plugins>
</build>
...

คำอธิบาย

  • r2dbc-postgresql เป็น dependency r2dbc สำหรับ postgresql
  • spring-boot-starter-data-r2dbc เป็น dependency สำหรับใช้ spring-data ร่วมกับ r2dbc

2. เขียน Main Class

@SpringBootApplication
public class AppStarter {

    public static void main(String[] args) {
        SpringApplication.run(AppStarter.class, args);
    }

}

3. กำหนด Config

classpath:application.properties

#---------------------------------- Logging ------------------------------------
logging.level.me.jittagornp=DEBUG
logging.level.org.springframework.data.r2dbc=DEBUG

#---------------------------------- R2dbc --------------------------------------
spring.r2dbc.url=r2dbc:postgresql://localhost/postgres?schema=public
spring.r2dbc.username=postgres
spring.r2dbc.password=password

4. เขียน Entity / Model

@Data
@Builder
public class UserEntity {

    private UUID id;

    private String username;

    private String firstName;

    private String lastName;
}

5. เขียน Repository

ในตัวอย่างนี้ เราจะ Manual Repository เอง

ประกาศ interface

public interface UserRepository {

    Flux<UserEntity> findAll();

    Mono<UserEntity> findById(final UUID id);

    Mono<UserEntity> create(final UserEntity entity);

    Mono<UserEntity> update(final UserEntity entity);

    Mono<Void> deleteAll();

    Mono<Void> deleteById(final UUID id);

}

implement interface

@Repository
@RequiredArgsConstructor
public class UserRepositoryImpl implements UserRepository {

    private final DatabaseClient databaseClient;

    @Override
    public Flux<UserEntity> findAll() {
        return databaseClient.sql("SELECT * FROM app.user")
                .map(this::convert)
                .all();
    }

    @Override
    public Mono<UserEntity> findById(final UUID id) {
        return databaseClient.sql("SELECT * FROM app.user WHERE id = :id")
                .bind("id", id)
                .map(this::convert)
                .one()
                .switchIfEmpty(Mono.error(new NotFoundException("User id \"" + id.toString() + "\"not found")));
    }

    @Override
    public Mono<UserEntity> create(final UserEntity entity) {
        entity.setId(UUID.randomUUID());
        return databaseClient.sql(
                "INSERT INTO app.user (id, username, first_name, last_name) " +
                "VALUES (:id, :username, :first_name, :last_name)"
        )
                .bind("id", entity.getId())
                .bind("username", entity.getUsername())
                .bind("first_name", entity.getFirstName())
                .bind("last_name", entity.getLastName())
                .then()
                .thenReturn(entity);
    }
    ...
}

หมายเหตุ

  • จากตัวอย่างด้านบน จะเห็นว่าเราใช้ DatabaseClient Manual Native SQL เองทั้งหมดเลย
  • บนหัว implmentation (class) แปะด้วย @Repository เพื่อบอกว่าอันนี้เป็น repository น่ะ

6. เขียน Controller

@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {

    private final UserRepository userRepository;

    @GetMapping
    public Flux<UserEntity> findAll() {
        return userRepository.findAll();
    }

    @GetMapping("/{id}")
    public Mono<UserEntity> findById(@PathVariable("id") final UUID id) {
        return userRepository.findById(id);
    }

    @ResponseStatus(HttpStatus.CREATED)
    @PostMapping
    public Mono<UserEntity> create(@RequestBody final UserEntity entity) {
        return userRepository.create(entity);
    }

    @PutMapping("/{id}")
    public Mono<UserEntity> update(@PathVariable("id") final UUID id, @RequestBody final UserEntity entity) {
        entity.setId(id);
        return userRepository.update(entity);
    }

    @ResponseStatus(HttpStatus.NO_CONTENT)
    @DeleteMapping("/{id}")
    public Mono<Void> deleteById(@PathVariable("id") final UUID id) {
        return userRepository.deleteById(id);
    }
}

7. Build Code

cd ไปที่ root ของ project จากนั้น

$ mvn clean package

8. Run

$ mvn spring-boot:run

9. ทดสอบด้วย Postman

Create User

Get all Users

Select Users from table