Проект Reactive Relational Database Connectivity (R2DBC) привносит API реактивного программирования в реляционные базы данных. io.r2dbc.spi.Connection из R2DBC предусматривает стандартный метод работы с неблокирующими подключениями к базе данных. Подключения передаются с помощью ConnectionFactory, аналогично DataSource в jdbc.

Конфигурацией ConnectionFactory управляют внешние конфигурационные свойства из spring.r2dbc.*. Например, можно объявить следующий раздел в application.properties:

Properties
spring.r2dbc.url=r2dbc:postgresql://localhost/test
spring.r2dbc.username=dbuser
spring.r2dbc.password=dbpass
Yaml
spring:
  r2dbc:
    url: "r2dbc:postgresql://localhost/test"
    username: "dbuser"
    password: "dbpass"
Задавать имя класса для драйвера не требуется, поскольку Spring Boot получает драйвер в результате обнаружения фабрики соединений R2DBC.
По крайней мере, должен быть указан URL-адрес. Информация, указанная в URL-адресе, имеет приоритет над отдельными свойствами, то есть name, username, password и параметрами объединения в пул.

Чтобы настроить соединения, создаваемые ConnectionFactory, то есть задать определенные параметры, которые вы не желаете (или не можете) настраивать в конфигурации вашей центральной базы данных, можно использовать помеченный аннотацией @Bean бин ConnectionFactoryOptionsBuilderCustomizer. В следующем примере показано, как вручную переопределить порт базы данных, в то время как остальные параметры будут браться из конфигурации приложения:

Java
import io.r2dbc.spi.ConnectionFactoryOptions;
import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyR2dbcConfiguration {
    @Bean
    public ConnectionFactoryOptionsBuilderCustomizer connectionFactoryPortCustomizer() {
        return (builder) -> builder.option(ConnectionFactoryOptions.PORT, 5432);
    }
}
Kotlin
import io.r2dbc.spi.ConnectionFactoryOptions
import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class MyR2dbcConfiguration {
    @Bean
    fun connectionFactoryPortCustomizer(): ConnectionFactoryOptionsBuilderCustomizer {
        return ConnectionFactoryOptionsBuilderCustomizer { builder ->
            builder.option(ConnectionFactoryOptions.PORT, 5432)
        }
    }
}

В следующих примерах показано, как установить некоторые параметры соединения PostgreSQL:

Java
import java.util.HashMap;
import java.util.Map;
import io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider;
import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyPostgresR2dbcConfiguration {
    @Bean
    public ConnectionFactoryOptionsBuilderCustomizer postgresCustomizer() {
        Map<String, String> options = new HashMap<>();
        options.put("lock_timeout", "30s");
        options.put("statement_timeout", "60s");
        return (builder) -> builder.option(PostgresqlConnectionFactoryProvider.OPTIONS, options);
    }
}
Kotlin
import io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider
import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class MyPostgresR2dbcConfiguration {
    @Bean
    fun postgresCustomizer(): ConnectionFactoryOptionsBuilderCustomizer {
        val options: MutableMap<String, String> = HashMap()
        options["lock_timeout"] = "30s"
        options["statement_timeout"] = "60s"
        return ConnectionFactoryOptionsBuilderCustomizer { builder ->
            builder.option(PostgresqlConnectionFactoryProvider.OPTIONS, options)
        }
    }
}

Если имеется бин ConnectionFactory, обычная автоконфигурация для DataSource из JDBC отключается. Если необходимо сохранить автоконфигурацию для DataSource из JDBC, и при этом вас устраивает риск использования блокирующего JDBC API в реактивном приложении, добавьте @Import(DataSourceAutoConfiguration.class) в класс, помеченный аннотацией @Configuration, в вашем приложении, чтобы повторно активировать ее.

Поддержка встроенных баз данных

Аналогично средствам поддержки JDBC фреймворк Spring Boot может автоматически конфигурировать встроенную базу данных для реактивного режима использования. URL-адреса любого подключения указывать не требуется. Необходимо только добавить зависимость сборки к встроенной базе данных, которую вы хотите использовать, как это показано в следующем примере:

<dependency>
    <groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-h2</artifactId>
    <scope>runtime</scope>
</dependency>

Если вы используете эту функцию в своих тестах, то можете заметить, что одна и та же база данных повторно используется всем вашим тестовым комплектом, независимо от количества контекстов приложения, которые вы используете. Если необходимо убедиться, что для каждого контекста имеется отдельная встроенная база данных, следует установить spring.r2dbc.generate-unique-name в true.

Использование DatabaseClient

Бин DatabaseClient является автоконфигурируемым, но вы можете привязать его через аннотацию @Autowire непосредственно к своим собственным бинам, как это показано в следующем примере:

Java
import java.util.Map;
import reactor.core.publisher.Flux;
import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
    private final DatabaseClient databaseClient;
    public MyBean(DatabaseClient databaseClient) {
        this.databaseClient = databaseClient;
    }
    // ...
    public Flux<Map<String, Object>> someMethod() {
        return this.databaseClient.sql("select * from user").fetch().all();
    }
}
Kotlin
import org.springframework.r2dbc.core.DatabaseClient
import org.springframework.stereotype.Component
import reactor.core.publisher.Flux
@Component
class MyBean(private val databaseClient: DatabaseClient) {
    // ...
    fun someMethod(): Flux<Map<String, Any>> {
        return databaseClient.sql("select * from user").fetch().all()
    }
}

Репозитории Spring Data R2DBC

Репозитории Spring Data R2DBC – это интерфейсы, которые можно определить для получения доступа к данным. Запросы создаются автоматически на основе имен методов. Например, интерфейс CityRepository может объявить метод findAllByState(String state) для поиска всех городов в данном штате.

В случае более сложных запросов можно аннотировать метод с помощью аннотации Query из Spring Data.

Репозитории Spring Data обычно расширяются за счет интерфейсов Repository или CrudRepository. Если вы используете автоконфигурацию, поиск в репозиториях ведется от пакета, содержащего основной конфигурационный класс (тот, который аннотирован @EnableAutoConfiguration или @SpringBootApplication), и вниз по иерархии.

В следующем примере показано типичное определение интерфейса взаимодействия с репозиторием Spring Data:

Java
import reactor.core.publisher.Mono;
import org.springframework.data.repository.Repository;
public interface CityRepository extends Repository<City, Long> {
    Mono<City> findByNameAndStateAllIgnoringCase(String name, String state);
}
Kotlin
import org.springframework.data.repository.Repository
import reactor.core.publisher.Mono
interface CityRepository : Repository<City?, Long?> {
    fun findByNameAndStateAllIgnoringCase(name: String?, state: String?): Mono<City?>?
}