diff --git a/persistence-modules/r2dbc/pom.xml b/persistence-modules/r2dbc/pom.xml
index f32b37974b..cfe344eba9 100644
--- a/persistence-modules/r2dbc/pom.xml
+++ b/persistence-modules/r2dbc/pom.xml
@@ -49,6 +49,34 @@
org.springframework.boot
spring-boot-devtools
+
+ org.projectlombok
+ lombok
+
+
+ org.springframework.boot
+ spring-boot-starter-data-r2dbc
+
+
+ org.postgresql
+ r2dbc-postgresql
+ ${r2dbc.postgresql.version}
+
+
+ org.postgresql
+ postgresql
+ ${jdbc.postgresql.version}
+
+
+ org.flywaydb
+ flyway-core
+ ${flyway.core.version}
+
+
+ io.r2dbc
+ r2dbc-h2
+ test
+
@@ -60,4 +88,10 @@
-
\ No newline at end of file
+
+ 9.14.1
+ 42.5.2
+ 1.0.0.RELEASE
+
+
+
diff --git a/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/SpringWebfluxFlywayApplication.java b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/SpringWebfluxFlywayApplication.java
new file mode 100644
index 0000000000..7eb3f8180c
--- /dev/null
+++ b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/SpringWebfluxFlywayApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.examples.r2dbc.flyway;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpringWebfluxFlywayApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringWebfluxFlywayApplication.class, args);
+ }
+
+}
diff --git a/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/config/DatabaseConfig.java b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/config/DatabaseConfig.java
new file mode 100644
index 0000000000..d4a4be93e9
--- /dev/null
+++ b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/config/DatabaseConfig.java
@@ -0,0 +1,23 @@
+package com.baeldung.examples.r2dbc.flyway.config;
+
+import org.flywaydb.core.Flyway;
+import org.springframework.boot.autoconfigure.flyway.FlywayProperties;
+import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@EnableConfigurationProperties({ R2dbcProperties.class, FlywayProperties.class })
+class DatabaseConfig {
+ @Bean(initMethod = "migrate")
+ public Flyway flyway(FlywayProperties flywayProperties, R2dbcProperties r2dbcProperties) {
+ return Flyway.configure()
+ .dataSource(flywayProperties.getUrl(), r2dbcProperties.getUsername(), r2dbcProperties.getPassword())
+ .locations(flywayProperties.getLocations()
+ .stream()
+ .toArray(String[]::new))
+ .baselineOnMigrate(true)
+ .load();
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/model/Department.java b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/model/Department.java
new file mode 100644
index 0000000000..cfb1555847
--- /dev/null
+++ b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/model/Department.java
@@ -0,0 +1,40 @@
+package com.baeldung.examples.r2dbc.flyway.model;
+
+import java.util.UUID;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.domain.Persistable;
+import org.springframework.data.relational.core.mapping.Table;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Table("department")
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class Department implements Persistable {
+
+ @Id
+ @JsonProperty("uuid")
+ @JsonAlias("id")
+ private UUID id;
+
+ @NotNull
+ @Size(max = 255, message = "The property 'name' must be less than or equal to 255 characters.")
+ private String name;
+
+ @Override
+ @JsonIgnore
+ public boolean isNew() {
+ return true;
+ }
+}
diff --git a/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/model/Student.java b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/model/Student.java
new file mode 100644
index 0000000000..94dbe6e97f
--- /dev/null
+++ b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/model/Student.java
@@ -0,0 +1,55 @@
+package com.baeldung.examples.r2dbc.flyway.model;
+
+import java.time.LocalDate;
+import java.util.UUID;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.domain.Persistable;
+import org.springframework.data.relational.core.mapping.Table;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Table("student")
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class Student implements Persistable {
+
+ @Id
+ @JsonProperty("uuid")
+ @JsonAlias("id")
+ private UUID id;
+
+ @NotNull
+ @Size(max = 255, message = "The property 'firstName' must be less than or equal to 255 characters.")
+ private String firstName;
+
+ @NotNull
+ @Size(max = 255, message = "The property 'lastName' must be less than or equal to 255 characters.")
+ private String lastName;
+
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
+ private LocalDate dateOfBirth;
+
+ @NotNull
+ private UUID department;
+
+ @Override
+ @JsonIgnore
+ public boolean isNew() {
+ return true;
+ }
+}
+
diff --git a/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/repository/DepartmentRepository.java b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/repository/DepartmentRepository.java
new file mode 100644
index 0000000000..85edf964b7
--- /dev/null
+++ b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/repository/DepartmentRepository.java
@@ -0,0 +1,10 @@
+package com.baeldung.examples.r2dbc.flyway.repository;
+
+import java.util.UUID;
+
+import org.springframework.data.repository.reactive.ReactiveCrudRepository;
+
+import com.baeldung.examples.r2dbc.flyway.model.Department;
+
+public interface DepartmentRepository extends ReactiveCrudRepository {
+}
diff --git a/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/repository/StudentRepository.java b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/repository/StudentRepository.java
new file mode 100644
index 0000000000..1fc3ae1641
--- /dev/null
+++ b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/repository/StudentRepository.java
@@ -0,0 +1,10 @@
+package com.baeldung.examples.r2dbc.flyway.repository;
+
+import java.util.UUID;
+
+import org.springframework.data.repository.reactive.ReactiveCrudRepository;
+
+import com.baeldung.examples.r2dbc.flyway.model.Student;
+
+public interface StudentRepository extends ReactiveCrudRepository {
+}
diff --git a/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/rest/DepartmentResource.java b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/rest/DepartmentResource.java
new file mode 100644
index 0000000000..32c3d5bbd8
--- /dev/null
+++ b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/rest/DepartmentResource.java
@@ -0,0 +1,29 @@
+package com.baeldung.examples.r2dbc.flyway.rest;
+
+import java.util.List;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.examples.r2dbc.flyway.model.Department;
+import com.baeldung.examples.r2dbc.flyway.repository.DepartmentRepository;
+
+import lombok.RequiredArgsConstructor;
+import reactor.core.publisher.Mono;
+
+@RestController
+@RequiredArgsConstructor
+public class DepartmentResource {
+
+ private final DepartmentRepository departmentRepository;
+
+ @GetMapping(path = "/department")
+ public Mono>> getDepartments() {
+ return departmentRepository.findAll()
+ .collectList()
+ .map(departments -> new ResponseEntity(departments, HttpStatus.OK));
+ }
+
+}
diff --git a/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/rest/StudentResource.java b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/rest/StudentResource.java
new file mode 100644
index 0000000000..624f673406
--- /dev/null
+++ b/persistence-modules/r2dbc/src/main/java/com/baeldung/examples/r2dbc/flyway/rest/StudentResource.java
@@ -0,0 +1,29 @@
+package com.baeldung.examples.r2dbc.flyway.rest;
+
+import javax.validation.Valid;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.examples.r2dbc.flyway.model.Student;
+import com.baeldung.examples.r2dbc.flyway.repository.StudentRepository;
+
+import lombok.RequiredArgsConstructor;
+import reactor.core.publisher.Mono;
+
+@RestController
+@RequiredArgsConstructor
+public class StudentResource {
+
+ private final StudentRepository studentRepository;
+
+ @PostMapping(path = "/student")
+ public Mono> createStudent(@RequestBody @Valid Mono createStudentRequest) {
+ return createStudentRequest.flatMap(studentRepository::save)
+ .map(student -> new ResponseEntity(student, HttpStatus.CREATED));
+ }
+
+}
diff --git a/persistence-modules/r2dbc/src/main/resources/application.yml b/persistence-modules/r2dbc/src/main/resources/application.yml
index bb47c7261c..919a088209 100644
--- a/persistence-modules/r2dbc/src/main/resources/application.yml
+++ b/persistence-modules/r2dbc/src/main/resources/application.yml
@@ -1,7 +1,14 @@
spring:
application:
name: r2dbc-test
-
+
+ r2dbc:
+ username: local
+ password: local
+ url: r2dbc:postgresql://localhost:8082/flyway-test-db
+ flyway:
+ url: jdbc:postgresql://localhost:8082/flyway-test-db
+ locations: classpath:db/postgres/migration
# R2DBC URL
r2dbc:
url: r2dbc:h2:mem://./testdb
diff --git a/persistence-modules/r2dbc/src/main/resources/db/docker-compose.yml b/persistence-modules/r2dbc/src/main/resources/db/docker-compose.yml
new file mode 100644
index 0000000000..5b95899e4b
--- /dev/null
+++ b/persistence-modules/r2dbc/src/main/resources/db/docker-compose.yml
@@ -0,0 +1,14 @@
+version: '3.9'
+networks:
+ obref:
+services:
+ postgres_db_service:
+ container_name: postgres_db_service
+ image: postgres:11
+ ports:
+ - "8082:5432"
+ hostname: postgres_db_service
+ environment:
+ - POSTGRES_PASSWORD=local
+ - POSTGRES_USER=local
+ - POSTGRES_DB=flyway-test-db
\ No newline at end of file
diff --git a/persistence-modules/r2dbc/src/main/resources/db/postgres/migration/V1_1__create_tables.sql b/persistence-modules/r2dbc/src/main/resources/db/postgres/migration/V1_1__create_tables.sql
new file mode 100644
index 0000000000..23ecd2ca59
--- /dev/null
+++ b/persistence-modules/r2dbc/src/main/resources/db/postgres/migration/V1_1__create_tables.sql
@@ -0,0 +1,17 @@
+
+CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
+
+CREATE TABLE IF NOT EXISTS department
+(
+ id uuid PRIMARY KEY UNIQUE DEFAULT uuid_generate_v4(),
+ name varchar(255)
+);
+
+CREATE TABLE IF NOT EXISTS student
+(
+ id uuid PRIMARY KEY UNIQUE DEFAULT uuid_generate_v4(),
+ first_name varchar(255),
+ last_name varchar(255),
+ date_of_birth DATE NOT NULL,
+ department uuid NOT NULL CONSTRAINT student_foreign_key1 REFERENCES department (id)
+);
\ No newline at end of file
diff --git a/persistence-modules/r2dbc/src/main/resources/db/postgres/migration/V1_2__insert_department.sql b/persistence-modules/r2dbc/src/main/resources/db/postgres/migration/V1_2__insert_department.sql
new file mode 100644
index 0000000000..62fa654d57
--- /dev/null
+++ b/persistence-modules/r2dbc/src/main/resources/db/postgres/migration/V1_2__insert_department.sql
@@ -0,0 +1,3 @@
+
+insert into department(name) values ('Computer Science');
+insert into department(name) values ('Biomedical');
\ No newline at end of file
diff --git a/persistence-modules/r2dbc/src/test/java/com/baeldung/examples/r2dbc/flyway/rest/StudentResourceUnitTest.java b/persistence-modules/r2dbc/src/test/java/com/baeldung/examples/r2dbc/flyway/rest/StudentResourceUnitTest.java
new file mode 100644
index 0000000000..455b6862f8
--- /dev/null
+++ b/persistence-modules/r2dbc/src/test/java/com/baeldung/examples/r2dbc/flyway/rest/StudentResourceUnitTest.java
@@ -0,0 +1,71 @@
+package com.baeldung.examples.r2dbc.flyway.rest;
+
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+import java.time.LocalDate;
+import java.util.List;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.web.reactive.function.BodyInserters;
+
+import com.baeldung.examples.r2dbc.flyway.model.Department;
+import com.baeldung.examples.r2dbc.flyway.model.Student;
+
+import reactor.core.publisher.Mono;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(webEnvironment = RANDOM_PORT)
+class StudentResourceUnitTest {
+
+ private static final String DEPARTMENT_ENDPOINT = "/department";
+ private static final String STUDENT_ENDPOINT = "/student";
+
+ @Autowired
+ protected WebTestClient webTestClient;
+
+ @Test
+ void givenDepartmentExists_WhenCreateStudentRequestIsSent_ShouldBeProcessedSuccessfully() {
+
+ // Given
+ List departmentList = webTestClient.get()
+ .uri(DEPARTMENT_ENDPOINT)
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectBodyList(Department.class)
+ .returnResult()
+ .getResponseBody();
+
+ Assertions.assertNotNull(departmentList);
+
+ // When
+ Student student = webTestClient.post()
+ .uri(STUDENT_ENDPOINT)
+ .body(BodyInserters.fromPublisher(Mono.just(Student.builder()
+ .firstName("John")
+ .lastName("Doe")
+ .dateOfBirth(LocalDate.of(2015, 12, 1))
+ .department(departmentList.get(0)
+ .getId())
+ .build()), Student.class))
+ .exchange()
+ .expectStatus()
+ .isEqualTo(201)
+ .expectBody(Student.class)
+ .returnResult()
+ .getResponseBody();
+
+ // Then
+ Assertions.assertNotNull(student.getId());
+ Assertions.assertEquals("John", student.getFirstName());
+ Assertions.assertEquals("Doe", student.getLastName());
+ Assertions.assertEquals(LocalDate.of(2015, 12, 1), student.getDateOfBirth());
+ }
+
+}
\ No newline at end of file
diff --git a/persistence-modules/r2dbc/src/test/resources/application.yml b/persistence-modules/r2dbc/src/test/resources/application.yml
index 8925116e4a..0903bed6fb 100644
--- a/persistence-modules/r2dbc/src/test/resources/application.yml
+++ b/persistence-modules/r2dbc/src/test/resources/application.yml
@@ -1,6 +1,29 @@
# R2DBC Test configuration
r2dbc:
url: r2dbc:h2:mem://./testdb
+
+server:
+ port: 8080
+
+spring:
+ r2dbc:
+ host: localhost
+ port: 8082
+ database: testdb
+ username: local
+ password: local
+
+ h2:
+ console:
+ enabled: true
+ path: h2-console
+
+
+ flyway:
+ url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;
+ user: local
+ password: local
+ locations: classpath:db/h2/migration
diff --git a/persistence-modules/r2dbc/src/test/resources/db/h2/migration/V1_1__create_tables.sql b/persistence-modules/r2dbc/src/test/resources/db/h2/migration/V1_1__create_tables.sql
new file mode 100644
index 0000000000..388b23fcf5
--- /dev/null
+++ b/persistence-modules/r2dbc/src/test/resources/db/h2/migration/V1_1__create_tables.sql
@@ -0,0 +1,19 @@
+
+CREATE TABLE department
+(
+ id uuid DEFAULT random_uuid() PRIMARY KEY UNIQUE NOT NULL,
+ name varchar(255)
+);
+
+CREATE TABLE student
+(
+ id uuid DEFAULT random_uuid() UNIQUE NOT NULL,
+ first_name varchar(255),
+ last_name varchar(255),
+ date_of_birth DATE NOT NULL,
+ department uuid NOT NULL
+);
+
+
+ALTER TABLE student
+ ADD FOREIGN KEY (department) REFERENCES department(id);
\ No newline at end of file
diff --git a/persistence-modules/r2dbc/src/test/resources/db/h2/migration/V1_2__insert_department.sql b/persistence-modules/r2dbc/src/test/resources/db/h2/migration/V1_2__insert_department.sql
new file mode 100644
index 0000000000..62fa654d57
--- /dev/null
+++ b/persistence-modules/r2dbc/src/test/resources/db/h2/migration/V1_2__insert_department.sql
@@ -0,0 +1,3 @@
+
+insert into department(name) values ('Computer Science');
+insert into department(name) values ('Biomedical');
\ No newline at end of file