// field notes 2019·11·01 archive

Getting started with Spring WebFlux and R2DBC

A short tour of Spring Data R2DBC and a reactive REST API in Kotlin — non-blocking access to a relational database with Spring Boot 2.2.

  • java
  • kotlin
  • spring-boot
  • webflux
  • r2dbc
  • tutorial

Originally published at dev.to .

Introduction

In this short article I will try to introduce you to R2DBC using Spring Data R2DBC and the world of reactive applications with Spring WebFlux.

What is R2DBC and why should you bother looking at it? R2DBC stands for Reactive Relational Database Connectivity. In simple words, R2DBC brings the possibility of non-blocking calls to relational databases (SQL).

Quoting r2dbc.io, R2DBC is in a nutshell:

  • Reactive Streams — R2DBC is founded on Reactive Streams providing a fully reactive non-blocking API.
  • Relational Databases — R2DBC engages SQL databases with a reactive API, something not possible with the blocking nature of JDBC.
  • Scalable Solutions — Reactive Streams makes it possible to move from the classic one-thread-per-connection approach to a more powerful, more scalable approach.
  • Open Specification — R2DBC is an open specification establishing a SPI that driver vendors can implement and clients can consume.

In this tutorial, we will build a simple REST API with Kotlin, Spring WebFlux, and connect to an H2 Database using Spring Data R2DBC.

Getting Started

If you want to easily get started with a Spring Boot project I recommend always using the Spring Initializr.

The Second Best Place on the Internet — Josh Long.

Gradle Configuration

First we have to configure our build.gradle.kts and include the dependencies:

// https://github.com/Petros0/springboot-full-reactive-kotlin/blob/master/build.gradle.kts
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    implementation("org.springframework.boot.experimental:spring-boot-starter-data-r2dbc")
    runtimeOnly("io.r2dbc:r2dbc-h2")
    // ...
}

For the complete build.gradle.kts see the repository.

Application Configuration

Spring Boot comes with auto-configurations for Spring Data R2DBC out-of-the-box. Below we leverage Spring Boot to create a connection to the database:

logging:
  level:
    org.springframework.data.r2dbc: DEBUG

spring:
  r2dbc:
    url: r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    name: sa
    password:

Then we create a table called Employee and we populate it with some data:

@Component
class ApplicationConfiguration {

    @Bean
    fun runner(employeeRepository: EmployeeRepository, db: DatabaseClient) = ApplicationRunner {
        val initDb = db.execute {
            """ CREATE TABLE employee (
                    id           SERIAL PRIMARY KEY,
                    first_name   VARCHAR(255) NOT NULL,
                    last_name    VARCHAR(255) NOT NULL
                );
            """
        }

        val stream = Stream.of(
                Employee(null, "Petros", "S")
        )

        val saveAll = employeeRepository.saveAll(Flux.fromStream(stream))

        initDb // initialize the database
                .then()
                .thenMany(saveAll) // then save our sample employees
                .subscribe() // execute
    }
}

While that may not seem really safe, in our application we don’t have any problems, since the database is created on startup and destroyed on shutdown.

REST API

We created our database and populated it with data — now what? Let’s make use of it and create a REST API.

Below is a simple domain model called Employee:

@Table("employee")
data class Employee(
    @Id val id: Long?,
    @Column("first_name") val firstName: String,
    @Column("last_name") val lastName: String,
)

Then, the repository, which will provide some queries out-of-the-box and one of our own:

interface EmployeeRepository : ReactiveCrudRepository<Employee, Long> {

    @Query("select e.* from Employee e where e.last_name = :lastName")
    fun findEmployeeByLastName(lastName: String): Mono<Employee>
}

Last but not least, the endpoint:

@RestController
@RequestMapping("/employees")
class EmployeeController(val repo: EmployeeRepository) {

    @GetMapping
    fun getAll() = repo.findAll()

    @GetMapping("/{lastName}")
    fun getByLastName(@PathVariable lastName: String) = repo.findEmployeeByLastName(lastName)

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    fun save(@RequestBody employee: Employee) = repo.save(employee)

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    fun delete(@PathVariable id: Long) = repo.deleteById(id)
}

Result

Run the application with ./gradlew bootRun. The application now runs on http://localhost:8080.

Closing thoughts

Spring Data R2DBC is still in an experimental stage. Although it is on the official release of Spring Boot 2.2.0, I would not recommend using it in production yet.

I hope you now have a better understanding of R2DBC.

You can find the repository here: Petros0/spring-boot-r2dbc-kotlin.

Resources