I have created sample project where i configured two datalayer's providers, blocking H2 in-memory and reactive H2 in memory: (checkout commit "blocking vs reactive refs, 6847756") https://github.com/ndopj/spring-boot-2.3.0-r2dbc-bug

Running application fails because ConnectionFactoryInitializer and ReactiveTransactionManager beans inside ManualReactiveConfiguration.class are loaded later than @PostConstruct method of service ReactiveService.class. As opposite ManualBlockingConfiguration.class contains @EnableJpaRepositories annotation with options entityManagerFactoryRef and transactionManagerRef which i guess load correct beans before any usage of jpa-repositories. I would expect same options for reactiveTransactionManager and ConnectionFactoryInitializer in case of @EnableR2dbcRepositories annotation.

Note: uncommenting controller under ApplicationLoader.class and commenting out @PostConstruct method of ReactiveService.class makes application runnable as expected since required beans are lazily loaded at "spring boot runtime" (also compare output logs between two cases). I have tried to reconfigure order of configuration but that doesn't seem to work. I have been following documentation: https://docs.spring.io/spring-data/r2dbc/docs/1.0.x/reference/html/#r2dbc.datbaseclient.transactions to create manual configuration of r2dbc data layer beans.

Comment From: ndopj

I made a research little bit about this and found out following: connectionFactory bean (in this case renamed to reactiveConnectionFactoryBean) is not really picked but is used by AbstracR2dncConfiguration.class to setup r2dbcDatabaseClient bean. Other beans from my manual configuration are picked later.

Looking into @EnableJpaRepositories annotation i mentioned we can see following:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(JpaRepositoriesRegistrar.class)
public @interface EnableJpaRepositories {
. . .
    /**
     * Configures the name of the {@link EntityManagerFactory} bean definition to be used to create repositories
     * discovered through this annotation. Defaults to {@code entityManagerFactory}.
     *
     * @return
     */
    String entityManagerFactoryRef() default "entityManagerFactory";

        String transactionManagerRef() ......
. . .
}

then looking into JpaRepositoryConfigExtension.class from org.springframework.data.jpa.repository.config we can see following

/**
 * JPA specific configuration extension parsing custom attributes from the XML namespace and
 * {@link EnableJpaRepositories} annotation. Also, it registers bean definitions for a
 * {@link PersistenceAnnotationBeanPostProcessor} (to trigger injection into {@link PersistenceContext}/
 * {@link PersistenceUnit} annotated properties and methods) as well as
 * {@link PersistenceExceptionTranslationPostProcessor} to enable exception translation of persistence specific
 * exceptions into Spring's {@link DataAccessException} hierarchy.
 *
 * @author Oliver Gierke
 * @author Eberhard Wolff
 * @author Gil Markham
 * @author Thomas Darimont
 * @author Christoph Strobl
 * @author Mark Paluch
 */
public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensionSupport {

    private static final Class<?> PAB_POST_PROCESSOR = PersistenceAnnotationBeanPostProcessor.class;
    private static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
    private static final String ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE = "enableDefaultTransactions";
    private static final String JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME = "org.springframework.data.jpa.util.JpaMetamodelCacheCleanup";
    private static final String ESCAPE_CHARACTER_PROPERTY = "escapeCharacter";
. . .
    @Override
    public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) {

        Optional<String> transactionManagerRef = source.getAttribute("transactionManagerRef");
        builder.addPropertyValue("transactionManager", transactionManagerRef.orElse(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME));
        builder.addPropertyValue("entityManager", getEntityManagerBeanDefinitionFor(source, source.getSource()));
        builder.addPropertyValue(ESCAPE_CHARACTER_PROPERTY, getEscapeCharacter(source).orElse('\\'));
        builder.addPropertyReference("mappingContext", JPA_MAPPING_CONTEXT_BEAN_NAME);
    }
. . .
}

so i guess this is how JpaRepositoryConfigExtension.class ensures that correct beans are picked whenever JpaRepository is used. In my case no such configuration exists and until my manual beans are picked default one's are used.

I would make a pull request but don't know if i am allowed to and this go to spring-framework dependencies probably as well. My temporary workaround is just not using reactive repositories inside @PostConstruct methods.

Comment From: snicoll

@ndopj thanks for the sample but please consider trying to trim it down next time. I'd also appreciate if you wouldn't use Java 14 at it is not required at all to reproduce the problem.

First of all, mixing R2DBC and JPA in the same app is highly unusual and will not give you the right semantics

Running application fails because

Rather than describing what you think is the cause, please describe the failure. I don't even know what the actual problem and stacktrace is. I've removed the blocking package and the reference to Spring Data JPA and the project still fails for me so I don't really understand how that's relevant.

This is what I have

2020-05-29 14:34:26.731  WARN 95435 --- [           main] onfigReactiveWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'reactiveService': Invocation of init method failed; nested exception is reactor.core.Exceptions$ErrorCallbackNotImplemented: org.springframework.data.r2dbc.BadSqlGrammarException: executeMany; bad SQL grammar [INSERT INTO model (name) VALUES ($1)]; nested exception is io.r2dbc.spi.R2dbcBadGrammarException: [42102] [42S02] Table "MODEL" not found; SQL statement:
INSERT INTO model (name) VALUES ($1) [42102-200]
2020-05-29 14:34:26.740  INFO 95435 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-05-29 14:34:26.749 ERROR 95435 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'reactiveService': Invocation of init method failed; nested exception is reactor.core.Exceptions$ErrorCallbackNotImplemented: org.springframework.data.r2dbc.BadSqlGrammarException: executeMany; bad SQL grammar [INSERT INTO model (name) VALUES ($1)]; nested exception is io.r2dbc.spi.R2dbcBadGrammarException: [42102] [42S02] Table "MODEL" not found; SQL statement:
INSERT INTO model (name) VALUES ($1) [42102-200]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:160) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:416) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1788) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]

Comment From: ndopj

Sorry for my issue structure, this is one of my first issues so i will do it better next time.

if you wouldn't use Java 14 at it is not required at all to reproduce the problem: as stated, sample isn't using any java 14 features, so one can just switch java version inside build.gradle (writing this for future readers)

First of all, mixing R2DBC and JPA in the same app is highly unusual and will not give you the right semantics: it might be seen as bad practice, but since r2dbc project supports only few DB vendors now and some JDBC drivers might not even have support in future i guess that lot of people will have to solve such problems with mixing blocking and reactive data-layers. Note documentation on dispatching blocking calls to another thread pool: https://projectreactor.io/docs/core/release/reference/#schedulers and the part Schedulers.boundedElastic() is a handy way to give a blocking process its own thread so that it does not tie up other resources. For example, in our project we are using not common DB provider (AWS Athena), i have manged to get it working with blocking spring boot data JPA starter. It would be shame to build whole project blocking just because of one DB provider and not benefit from reactive stack at all since blocking calls can be dispatched or somehow simulated as asynchronous (i now that dispatching to thread pool workaround is not real async or reactive behavior).

I am sorry that i forgot to provide my stack trace:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'reactiveService': Invocation of init method failed; nested exception is reactor.core.Exceptions$ErrorCallbackNotImplemented: org.springframework.data.r2dbc.BadSqlGrammarException: executeMany; bad SQL grammar [INSERT INTO model (name) VALUES ($1)]; nested exception is io.r2dbc.spi.R2dbcBadGrammarException: [42102] [42S02] Tabuľka "MODEL" nenájdená
Table "MODEL" not found; SQL statement:
INSERT INTO model (name) VALUES ($1) [42102-200]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:160) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:416) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1788) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]

as you can see its exactly same as yours so configuration of blocking data layer doesn't affect behavior in this case at all. Note: i have updated git repository with transactionManager references so blocking and reactive transaction manager beans doesn't clash when called since they both share same interface.

Exception happens because ConnectionFactoryInitializer bean from ManualReactiveConfiguration.class responsible for creating tables is not picked at the time of call to @PostConstruct init() method from service ReactiveService.class. As opposite i introduced how @EnableJpaRepositories annotation provides support for referencing which EntityManagerFactory and TransactionManager beans should be picked as default ones so when JPA call is executed, correct beans are loaded. This references also provide support for multiple blocking data layer configurations (from @EnableJpaRepositories referenced EntityManagerFactory bean is used in Repository interfaces placed in packages for which that bean configuration was created). Note the basePackages (where this configuration should be used) and entityManagerFactoryRef (which EntityManagerFactory should be used in that packages):

. . .
@EnableJpaRepositories(basePackages = {"com.blocking"},
        entityManagerFactoryRef = "blockingEntityManagerFactory",
        transactionManagerRef = "blockingTransactionManager")
public class ManualBlockingConfiguration {
. . .
}

Update: In mean time i have found workaround directly from documentation that references this problem: https://docs.spring.io/spring-data/r2dbc/docs/1.0.x/reference/html/#r2dbc.init and found also my own workaround which in my case is more easy to do then proposed ones by doing following:

@Configuration
@EnableTransactionManagement
@PropertySource(value = "classpath:application.properties")
@EnableR2dbcRepositories(basePackages = {"com.reactive"})
public class ManualReactiveConfiguration extends AbstractR2dbcConfiguration {
. . .
    @Primary
    @Bean("reactiveConnectionFactoryInitializer")
    public ConnectionFactoryInitializer initializer(ConnectionFactory reactiveConnectionFactory) {
        . . .
    }
. . .
}

and inside class using reactive repository interface within @PostConstruct mehtod (note @DependsOn annotation):

@Service
@DependsOn("reactiveConnectionFactoryInitializer", "reactiveTransactionManager")
@Transactional(transactionManager = "reactiveTransactionManager")
public class ReactiveService {
. . .
    @PostConstruct
    void init() {
        reactiveRepository.save(new ReactiveModel())
                .subscribe(reactiveModel -> LoggerFactory.getLogger(this.getClass())
                        .info("Saved reactive model with ID {}", reactiveModel.getId()));
    }
}

This structure will force to load ConnectionFactoryInitializer bean before @PostConstruct init() method is loaded. On the other hand it has disadvantage of specifying @DependsOn annotation at each similar case. So you may close this issue after-all, but i don't see any reason why @EnableR2dbcRepositories should not contain ConnectionFactoryInitalizerRef option which would "bind" provided ConnectionFactoryInitializer bean with that specific R2dbc configuration over specific package and specific ConnectionFactory so its instantiated at right time and not on its own, similarly to how transactionManagerRef and entityManagerRef works in case of @EnableJpaRepositories. That would ease development and no mentioned workaround would be needed.

Note that this is my final stack trace (see "[blocking data config]" and "[reactive data config]" logs. While blocking configurations does correct bean instantiation thanks to mentioned Refs options, in case of reactive configuration i need to use @DependsOn annotation to force pre-loading correct beans):

2020-05-30 19:01:18.405  INFO 21360 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 40ms. Found 1 JPA repository interfaces.
2020-05-30 19:01:18.406  INFO 21360 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2020-05-30 19:01:18.406  INFO 21360 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data R2DBC repositories in DEFAULT mode.
2020-05-30 19:01:18.412  INFO 21360 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 5ms. Found 1 R2DBC repository interfaces.
2020-05-30 19:01:18.788  INFO 21360 --- [           main] uration$$EnhancerBySpringCGLIB$$1427ada8 : [blocking data config] blocking datasource init
2020-05-30 19:01:18.847  INFO 21360 --- [           main] uration$$EnhancerBySpringCGLIB$$1427ada8 : [blocking data config] blocking entity manager factory init
2020-05-30 19:01:18.868  INFO 21360 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: blocking]
2020-05-30 19:01:18.898  INFO 21360 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.15.Final
2020-05-30 19:01:18.989  INFO 21360 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-05-30 19:01:19.077  INFO 21360 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-05-30 19:01:19.149  INFO 21360 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-05-30 19:01:19.160  INFO 21360 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2020-05-30 19:01:19.528  INFO 21360 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-05-30 19:01:19.532  INFO 21360 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'blocking'
2020-05-30 19:01:19.693  INFO 21360 --- [           main] uration$$EnhancerBySpringCGLIB$$1427ada8 : [blocking data config] blocking transaction manager init
2020-05-30 19:01:19.721  INFO 21360 --- [           main] com.blocking.BlockingService             : Save blocking model with ID 1
2020-05-30 19:01:19.726  INFO 21360 --- [           main] uration$$EnhancerBySpringCGLIB$$d4b64e28 : [reactive data config] Reactive connection factory init
2020-05-30 19:01:19.733  INFO 21360 --- [           main] uration$$EnhancerBySpringCGLIB$$d4b64e28 : [reactive data config] Reactive connection factory initializer init
2020-05-30 19:01:19.799  INFO 21360 --- [           main] uration$$EnhancerBySpringCGLIB$$d4b64e28 : [reactive data config] reactive transaction manager init
2020-05-30 19:01:19.914  INFO 21360 --- [           main] com.reactive.ReactiveService             : Saved reactive model with ID 1
2020-05-30 19:01:20.227  INFO 21360 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2020-05-30 19:01:20.235  INFO 21360 --- [           main] com.ApplicationLauncher                  : Started ApplicationLauncher in 3.178 seconds (JVM running for 3.734)

Comment From: snicoll

as stated, sample isn't using any java 14 features

Then don't require that source code level please, it just makes it harder for someone to run the sample if they don't have installed Java 14 yet.

In mean time i have found workaround directly from documentation that references this problem

It isn't a workaround but how you are supposed to configure things. If you want a certain bean to be initialized before another one you need to declare a dependency one way or the other. The Spring Boot reference guide has also a dedicated section that showcases how to initialize the database.