SoFunction
Updated on 2025-03-11

Springboot3+r2dbc responsive programming practice

Spring boot3 is already M1, and recently the group of people are also starting to start working on Reactive+Spring Boot3. Following everyone's pace, I will also get a starter of the project. We will use java17+Spring Boot3+r2dbc+Reactive stack to tell you. Everyone is welcome to discuss it. (For responsiveness, please asynchronously into the previous article for a detailed introduction.)

r2dbc

Reactor also has a Spring WebFlux framework based on it. Including reactive technologies such as rxjava. We actually already have many excellent responsive processing frameworks at the application layer.

But there is a problem that all frameworks need to obtain the underlying data, and basically the underlying reading and writing of relational databases are still synchronized.

To solve this problem, two standards emerged, one is ADBC ​​(Asynchronous Database Access API) proposed by oracle, and the other is R2DBC (Reactive Relational Database Connectivity) proposed by Pivotal.

R2DBC is designed based on the Reactive Streams standard. By using R2DBC, you can use the reactive API to manipulate data.

At the same time, R2DBC is just an open standard, and each specific database connection implementation needs to implement this standard.

Today, we will use r2dbc-h2 as an example to explain the use of r2dbc in Spring webFlux.

Engineering dependency

Here is a list

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="/POM/4.0.0" xmlns:xsi="http:///2001/XMLSchema-instance"
    xsi:schemaLocation="/POM/4.0.0 /xsd/maven-4.0.">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId></groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.0-M1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId></groupId>
    <artifactId>springboot3demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot3demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <>17</>
    </properties>
    <dependencies>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-data-r2dbc</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-groovy-templates</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-hateoas</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

        <dependency>
            <groupId>io.r2dbc</groupId>
            <artifactId>r2dbc-h2</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId></groupId>
            <artifactId>reactor-test</artifactId>
<!--            <version>3.4.14</version>-->
<!--            <scope>compile</scope>-->
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId></groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </pluginRepository>
    </pluginRepositories>

</project>

Configuration File

Here we only configure r2dbc link information

Configuration class

Used to configure default links and create initialized data

package .;

import ;
import io.;
import io.;
import io.;
import ;
import ;
import ;
import ;
import ;
import ;
import static io..*;

@Configuration
@ConfigurationProperties(prefix = "r2dbc")
public class DBConfig {

    private String url;
    private String user;
    private String password;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
         = url;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
         = user;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
         = password;
    }

    @Bean
    public ConnectionFactory connectionFactory() {
        ("url ==> "+url);
        ConnectionFactoryOptions baseOptions = (url);
         ob = ().from(baseOptions);
        if (!(user)) {
            ob = (USER, user);
        }
        if (!(password)) {
            ob = (PASSWORD, password);
        }
        return (());
    }

    @Bean
    public CommandLineRunner initDatabase(ConnectionFactory cf) {

        return (args) ->
                (())
                        .flatMap(c ->
                                (()
                                                .add("drop table if exists Users")
                                                .add("create table Users(" +
                                                        "id IDENTITY(1,1)," +
                                                        "firstname varchar(80) not null," +
                                                        "lastname varchar(80) not null)")
                                                .add("insert into Users(firstname,lastname)" +
                                                        "values('Jacky','Li')")
                                                .add("insert into Users(firstname,lastname)" +
                                                        "values('Doudou','Li')")
                                                .add("insert into Users(firstname,lastname)" +
                                                        "values('Maimai','Li')")
                                                .execute())
                                        .doFinally((st) -> ())
                        )
                        .log()
                        .blockLast();
    }

}

bean

Create user bean

package .;

import ;

public class Users {
    @Id
    private Long id;
    private String firstname;
    private String lastname;

    public Users(){

    }

    public Users(Long id, String firstname, String lastname) {
         = id;
         = firstname;
         = lastname;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
         = id;
    }

    public String getFirstname() {
        return firstname;
    }

    public void setFirstname(String firstname) {
         = firstname;
    }

    public String getLastname() {
        return lastname;
    }

    public void setLastname(String lastname) {
         = lastname;
    }


    @Override
    public String toString() {
        return "User{" +
                ", firstname='" + firstname + '\'' +
                ", lastname='" + lastname + '\'' +
                '}';
    }
}

DAO

The dao code list is as follows, including query list, query by id, and creating user

package .;

import io.;
import io.;
import ..R2dbcEntityTemplate;
import ;
import ;
import ;
import ;
import .;

import static .;
import static ;

@Component
public class UsersDao {
    private ConnectionFactory connectionFactory;
    private R2dbcEntityTemplate template;

    public UsersDao(ConnectionFactory connectionFactory) {
         = connectionFactory;
         = new R2dbcEntityTemplate(connectionFactory);
    }

    public Mono<Users> findById(long id) {

        return (query(where("id").is(id)),);

//        return (())
//                .flatMap(c -> (("select id,firstname,lastname from Users where id = $1")
//                                .bind("$1", id)
//                                .execute())
//                        .doFinally((st) -> close(c)))
//                .map(result -> ((row, meta) ->
//                        new Users(("id", ),
//                                ("firstname", ),
//                                ("lastname", ))))
//                .flatMap( p -> (p));
    }

    public Flux<Users> findAll() {
        return ().all();
//        return (())
//                .flatMap((c) -> (("select id,firstname,lastname from users")
//                                .execute())
//                        .doFinally((st) -> close(c)))
//                .flatMapMany(result -> (((row, meta) -> {
//                    Users acc = new Users();
//                    (("id", ));
//                    (("firstname", ));
//                    (("lastname", ));
//                    return acc;
//                })));
    }

    public Mono<Users> createAccount(Users account) {

        return (())
                .flatMap(c -> (())
                        .then((("insert into Users(firstname,lastname) values($1,$2)")
                                .bind("$1", ())
                                .bind("$2", ())
                                .returnGeneratedValues("id")
                                .execute()))
                        .map(result -> ((row, meta) ->
                                new Users(("id", ),
                                        (),
                                        ())))
                        .flatMap(pub -> (pub))
                        .delayUntil(r -> ())
                        .doFinally((st) -> ()));

    }

    private <T> Mono<T> close(Connection connection) {
        return (())
                .then(());
    }
}

controller

Controller code list is as follows, including query list, query by id, and creating user, etc.

package .;

import ;
import ;
import ;
import ;
import .*;
import ;
import ;
import .;
import .;

@RestController
public class UsersController {
    @Autowired
    private final UsersDao usersDao;

    public UsersController(UsersDao usersDao) {
         = usersDao;
    }

    @GetMapping("/users/{id}")
    public Mono<ResponseEntity<Users>> getUsers(@PathVariable("id") Long id) {

        return (id)
                .map(acc -> new ResponseEntity<>(acc, ))
                .switchIfEmpty((new ResponseEntity<>(null, HttpStatus.NOT_FOUND)));
    }

    @GetMapping("/users")
    public Flux<Users> getAllAccounts() {
        return ();
    }

    @PostMapping("/createUser")
    public Mono<ResponseEntity<Users>> createUser(@RequestBody Users user) {
        return (user)
                .map(acc -> new ResponseEntity<>(acc, ))
                .log();
    }
}

Startup class manifest:

package .springboot3demo;


import ;
import ;
import ;
import .;


@SpringBootApplication
@EnableConfigurationProperties()
public class WebFluxR2dbcApp {
    public static void main(String[] args) {
        (, args);
    }
}

OK, so we have completed the entire demo

Reference link:

/p/299069835

This is the article about springboot3+r2dbc responsive programming practice. For more related springboot3 r2dbc responsive programming content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!