Search code examples
springmavenspring-webfluxspring-data-r2dbcdbclient

Problem extending Spring WebFlux application with reactive DB client: conflicting Maven dependencies?


I have a small working Spring WebFlux app made basing on this code:

https://github.com/chang-chao/spring-webflux-reactive-jdbc-sample

As far as I've understood this is some kind of mix between purely reactive programming and usual blocking relational databases.

Now I have a task to add reactive DB client to my app. I stared with this guide:

https://spring.io/guides/gs/accessing-data-r2dbc/

But as soon as I added following dependencies to my pom.xml:

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

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

my WORKING app failed to start saying it couldn't find autowired repository bean. This error disappeared as soon as I've removed two dependencies above.

Initial full pom.xml of WORKING app:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.freelance</groupId>
    <artifactId>studentlist</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>student-list</name>
    <description>Spring WebFlux application for managing students</description>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.3.4.RELEASE</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <version>3.3.10.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

I suppose some dependencies are conflicting with each other. Is there a way to actually use these dependencies with all my other dependencies so I'll be able to follow that guide?

I don't know if app's Java code is needed for this question, and if it is, which pieces. For now I'll just add application.properties:

spring.h2.console.enabled=true
spring.h2.console.path=/h2_console

spring.datasource.url=jdbc:h2:~/studentlist
spring.datasource.platform=h2
spring.datasource.initialization-mode=always
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driverClassName=org.h2.Driver

spring.jpa.generate-ddl=true
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto = update
spring.jpa.show-sql=true

logging.level.org.springframework=warn
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace
logging.level.org.hibernate.SQL=warn
logging.level.io.netty=warn

spring.datasource.maximum-pool-size=100

Adding either of these two dependencies, spring-boot-starter-data-r2dbc or r2dbc-h2, without the second one is enough to cause this error:

2020-10-20 15:31:26.008  WARN 11580 --- [           main] onfigReactiveWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'studentController': Unsatisfied dependency expressed through field 'studentService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'studentService': Unsatisfied dependency expressed through field 'studentRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.freelance.studentlist.repository.StudentRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
2020-10-20 15:31:26.123 ERROR 11580 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field studentRepository in com.freelance.studentlist.service.StudentServiceImpl required a bean of type 'com.freelance.studentlist.repository.StudentRepository' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'com.freelance.studentlist.repository.StudentRepository' in your configuration.


Process finished with exit code 1

I removed JPA dependency and I'm currently trying to make things work with just r2dbc.

I suppose my application.properties would no longer be valid. Could you please help me with changing it? I use this code fragment from the guide:

public class R2DBCConfiguration extends AbstractR2dbcConfiguration {
    @Bean
    public H2ConnectionFactory connectionFactory() {
        return new H2ConnectionFactory(
                H2ConnectionConfiguration.builder()
                        .url("jdbc:h2:~/studentlist;DB_CLOSE_DELAY=-1;TRACE_LEVEL_FILE=4")
                        .username("sa")
                        .build());
    }
}

I strongly suspect that such url is not valid for r2dbc. What would be valid substitution for jdbc:h2:~/studentlist URL in case of r2dbc H2 (not in-memory, just local DB)?

How should I change this block of code in application.properties ? URL in particular!

**spring.datasource.url=jdbc:h2:~/studentlist**
spring.datasource.platform=h2
spring.datasource.initialization-mode=always
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driverClassName=org.h2.Driver

Please help if you know! Can't google an appropriate example for now...


Solution

  • The spring.datasource is for the traditional Jdbc data source, to configure an R2dbc connection, use spring.r2dbc prefix instead in Spring Boot application.properties.

    Check my Spring R2dbc example, if you are new to Spring Data R2dbc, the docs in Readme.md.