I introduced you to the Reactive Relational Database Connectivity (R2DBC) specs, about the Service Provider Interface library and the drivers that implement this SPI library in the previous tutorial. If your application uses Spring framework and wants to manipulate the database in a reactive mechanism, you can use Spring Data R2DBC to make manipulating the database more convenient and easier. How is it in detail? Let’s find out together in this tutorial!
First, I will create a new Maven project as an example:
I will use the PostgreSQL database as an example, so I will declare the PostgreSQL R2DBC driver and Spring Data R2DBC dependency as follows:
1 2 3 4 5 6 7 8 9 10 |
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-r2dbc</artifactId> <version>3.1.1</version> </dependency> <dependency> <groupId>io.r2dbc</groupId> <artifactId>r2dbc-postgresql</artifactId> <version>0.8.13.RELEASE</version> </dependency> |
In the previous tutorial, I mentioned the object of the ConnectionFactory class to declare connection information to the database server, here is the PostgreSQL database. With Spring Data R2DBC, you also need to initialize the object of this ConnectionFactory class, as a bean in the Spring container.
Spring Data R2DBC provides us with an abstract class named AbstractR2dbcConfiguration to configure the beans needed for us to work with the database in a reactive manner.
In this AbstractR2dbcConfiguration class, you will see that it defines an abstract method to create a new object of the ConnectionFactory class. You need to create a new class that extends the AbstractR2dbcConfiguration class to define the bean for the ConnectionFactory class! My example is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
package com.huongdanjava.springdatar2dbc; import io.r2dbc.spi.ConnectionFactories; import io.r2dbc.spi.ConnectionFactory; import io.r2dbc.spi.ConnectionFactoryOptions; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration; import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; @Configuration @EnableR2dbcRepositories public class DatabaseConfiguration extends AbstractR2dbcConfiguration { @Bean public ConnectionFactory connectionFactory() { ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() .option(ConnectionFactoryOptions.DRIVER, "postgresql") .option(ConnectionFactoryOptions.HOST, "localhost") .option(ConnectionFactoryOptions.USER, "khanh") .option(ConnectionFactoryOptions.PASSWORD, "123456") .option(ConnectionFactoryOptions.DATABASE, "spring_data_r2dbc_example") .build(); return ConnectionFactories.get(options); } } |
As you can see, here I also declare using the @EnableR2dbcRepositories annotation. The purpose of this annotation, similar to the @EnableJpaRepositories annotation in Spring Data JPA, will help Spring automatically scan and initialize beans for Spring Data R2DBC Repository classes. Annotation @EnableR2dbcRepositories allows us to configure the package to scan with the basePackages or basePackageClasses attribute, if you do not declare these attributes, by default Spring will scan the package containing the annotated class with the @EnableR2dbcRepositories annotation!
Next, we will define the application-related Repository classes.
As an example, I will define the “student” table:
1 2 3 4 5 |
CREATE TABLE student ( id INT NOT NULL, name VARCHAR(50) DEFAULT NULL, PRIMARY KEY (id) ) |
with the following content in the PostgreSQL database:
The model class for the “student” table, I will define it as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package com.huongdanjava.springdatar2dbc; import org.springframework.data.annotation.Id; import org.springframework.data.relational.core.mapping.Table; @Table public class Student { private static final long serialVersionUID = 1L; @Id private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } |
Similar to Spring Data JPA, you can define a Repository class with Spring Data R2DBC for the “student” table as follows:
1 2 3 4 5 6 7 8 9 |
package com.huongdanjava.springdatar2dbc; import org.springframework.data.r2dbc.repository.R2dbcRepository; import org.springframework.stereotype.Repository; @Repository public interface StudentRepository extends R2dbcRepository<Student, Long> { } |
At this point, we have configured Spring Data R2DBC for our application already!
To check the results, I will write a class with the main() method with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.huongdanjava.springdatar2dbc; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Application { public static void main(String[] args) throws InterruptedException { ApplicationContext ac = new AnnotationConfigApplicationContext(DatabaseConfiguration.class); StudentRepository studentRepository = (StudentRepository) ac.getBean("studentRepository"); studentRepository.findAll().log() .map(s -> { System.out.println(s.getName()); return s; }) .subscribe(); Thread.sleep(10000); } } |
The result when we run the above example is as follows: