3

Im trying to fetch a list of objects from the database using Spring boot Webflux with the postgres R2DBC driver, but I get an error saying:

value ignored org.springframework.transaction.reactive.TransactionContextManager$NoTransactionInContextException: No transaction in context Context1{reactor.onNextError.localStrategy=reactor.core.publisher.OnNextFailureStrategy$ResumeStrategy@7c18c255}

it seems all DatabaseClient operations requires to be wrap into a transaction.

I tried different combinations of the dependencies between spring-boot-data and r2db but didn't really work.

Version:

        <spring-boot.version>2.2.0.RC1</spring-boot.version>
        <spring-data-r2dbc.version>1.0.0.BUILD-SNAPSHOT</spring-data-r2dbc.version>
        <r2dbc-releasetrain.version>Arabba-M8</r2dbc-releasetrain.version>

Dependencies:

<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-r2dbc</artifactId>
  <version>${spring-data-r2dbc.version}</version>
</dependency>
<dependency>
  <groupId>io.r2dbc</groupId>
  <artifactId>r2dbc-postgresql</artifactId>
</dependency>
<dependency>
  <groupId>org.postgresql</groupId>
  <artifactId>postgresql</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
  <dependency>
    <groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-bom</artifactId>
    <version>${r2dbc-releasetrain.version}</version>
    <type>pom</type>
    <scope>import</scope>
  </dependency>
</dependencies>
</dependencyManagement>
fun findAll(): Flux<Game> {
    val games = client
        .select()
        .from(Game::class.java)
        .fetch()
        .all()
        .onErrorContinue{ throwable, o -> System.out.println("value ignored $throwable $o") }
    games.subscribe()
    return Flux.empty()
}

@Table("game")
data class Game(@Id val id: UUID = UUID.randomUUID(),
                @Column("guess") val guess: Int = Random.nextInt(500))

Github repo: https://github.com/odfsoft/spring-boot-guess-game/tree/r2dbc-issue

I expect read operations to not require @Transactional or to run the query without wrapping into the transactional context manually.

UPDATE: After a few tries with multiple version I manage to find a combination that works:

<spring-data-r2dbc.version>1.0.0.BUILD-SNAPSHOT</spring-data-r2dbc.version>
<r2dbc-postgres.version>0.8.0.RC2</r2dbc-postgres.version>

Dependencies:

<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-r2dbc</artifactId>
  <version>${spring-data-r2dbc.version}</version>
</dependency>
<dependency>
  <groupId>io.r2dbc</groupId>
  <artifactId>r2dbc-postgresql</artifactId>
  <version>${r2dbc-postgres.version}</version>
</dependency>
<dependencyManagement>
<dependencies>
  <dependency>
    <groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-bom</artifactId>
    <version>Arabba-RC2</version>
    <type>pom</type>
    <scope>import</scope>
  </dependency>
</dependencies>
</dependencyManagement>

it seems the versions notation went from 1.0.0.M7 to 0.8.x for r2dbc due to the following:

but after updating to the latest version a new problem appear which is that a transaction is required to run queries as follow:

Update configuration:

@Configuration
class PostgresConfig : AbstractR2dbcConfiguration() {

    @Bean
    override fun connectionFactory(): ConnectionFactory {
        return PostgresqlConnectionFactory(
            PostgresqlConnectionConfiguration.builder()
                .host("localhost")
                .port(5432)
                .username("root")
                .password("secret")
                .database("game")
                .build())
    }

    @Bean
    fun reactiveTransactionManager(connectionFactory: ConnectionFactory): ReactiveTransactionManager {
        return R2dbcTransactionManager(connectionFactory)
    }

    @Bean
    fun transactionalOperator(reactiveTransactionManager: ReactiveTransactionManager) =
        TransactionalOperator.create(reactiveTransactionManager)

}

Query:

    fun findAll(): Flux<Game> {
        return client
            .execute("select id, guess from game")
            .`as`(Game::class.java)
            .fetch()
            .all()
            .`as`(to::transactional)
            .onErrorContinue{ throwable, o -> System.out.println("value ignored $throwable $o") }
            .log()
    }

Disclaimer this is not mean to be used in production!! still before GA.

Folger Fonseca
  • 183
  • 1
  • 1
  • 11

0 Answers0