3

I'm working on an existing Spring Boot 2.4 project and trying to bring in Webflux and R2DBC into an existing JPA based app.

I'm able to bring in the Webflux dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

without any issues, my app starts and behaves as it should, was able to perform some tests using Flux/Mono and all is good.

To get full reactivity however, I need to go all the way down to the persistence layer, i.e. also bringing in R2DBC dependency (for postgres)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-postgresql</artifactId>
    <version>0.8.6.RELEASE</version>
</dependency>

I have the following as my configuration class:

@Configuration
@EnableR2dbcRepositories(
        repositoryBaseClass = ReactiveCrudRepository.class
)
public class R2DBCConfiguration extends AbstractR2dbcConfiguration {

    @Bean
    @Qualifier("rxConnectionFactory")
    @Override
    public ConnectionFactory connectionFactory() {
        return new PostgresqlConnectionFactory(
                PostgresqlConnectionConfiguration
                .builder()
                .username("user")
                .password("password")
                .host("localhost")
                .port(5432)
                .database("my_db")
                .build()
        );
    }
}

With this dependency, my project fails to start, consistently giving the same exception:

Caused by: java.lang.IllegalStateException: Cannot apply reactive transaction to non-reactive return type: interface java.util.List
    at org.springframework.transaction.interceptor.TransactionAspectSupport.lambda$invokeWithinTransaction$0(TransactionAspectSupport.java:351)
    at java.util.concurrent.ConcurrentMap.computeIfAbsent(ConcurrentMap.java:324)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:346)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:134)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
    at com.mycompany.myapplication.service.SettingsServiceImpl$$EnhancerBySpringCGLIB$$5198d258.findAll(<generated>)
    at com.mycompany.myapplication.service.RuntimeSettingsServiceImpl.populateMap(RuntimeSettingsServiceImpl.java:96)
    at com.mycompany.myapplication.service.RuntimeSettingsServiceImpl.init(RuntimeSettingsServiceImpl.java:65)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:389)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:333)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:157)
    ... 109 common frames omitted

I'm failing to understand why this is happening - we do use the traditional SimpleJpaRepository implementation for our DAOs / none of which extend ReactiveCrudRepository and thus should not be "seen" as reactive stores from the app point of view.

In any case I was wondering if others have faced a similar scenario and a possible workaround/fix.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
cudiaco
  • 432
  • 1
  • 6
  • 21
  • well how does the JPA repo know what connection to use? Your exception points to the `SettingsServiceImpl` and it says it is using the reactive jdbc connection to do its transaction. I dont have any solution for you, but no one will be able to help you since you have not produced any code to be able to reproduce the problem. All of the posted code works, post code that is not working, or enough code to reproduce the problem. Voted to close. – Toerktumlare Nov 20 '20 at 19:41

1 Answers1

3

Check my answer to this question, and I also provided a working example.

BTW, I do not think it is a good option mixed blocking JPA APIs and none-blocking R2dbc APIs in the sample application.

Hantsy
  • 8,006
  • 7
  • 64
  • 109
  • Thank you! My intended design is to use the Reactive APIs separately for certain use cases which will be all read-only operations. – cudiaco Nov 23 '20 at 14:32
  • Could you explain why it is not a goot option? I'm working also on project with JDBC and traying to add R2DBC support. I want to use JPA for editing/inserting records and R2DBC for returning Flux instead of List. Is it a metter of performace, memory/cpu consuption or maybe other problems? – Olek Mar 27 '21 at 08:05