// 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.
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.