Search code examples
spring-bootspring-dataspring-webfluxspring-data-r2dbc

Extending ReactiveQueryByExampleExecutor throws PropertyReferenceException


I'm having an issue with trying to implement ReactiveQueryByExampleExecutor into my repository in my application

Stack trace:

    Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract reactor.core.publisher.Mono org.springframework.data.repository.query.ReactiveQueryByExampleExecutor.exists(org.springframework.data.domain.Example)! No property exists found for type Solution!
        at org.springframework.data.r2dbc.repository.query.PartTreeR2dbcQuery.<init>(PartTreeR2dbcQuery.java:70)
        at org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactory$R2dbcQueryLookupStrategy.resolveQuery(R2dbcRepositoryFactory.java:187)
        at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lookupQuery(QueryExecutorMethodInterceptor.java:99)
        at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lambda$mapMethodsToQuery$1(QueryExecutorMethodInterceptor.java:92)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
        at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
        at java.base/java.util.Collections$UnmodifiableCollection$1.forEachRemaining(Collections.java:1052)
        at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
        at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
        at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.mapMethodsToQuery(QueryExecutorMethodInterceptor.java:94)
        at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lambda$new$0(QueryExecutorMethodInterceptor.java:84)
        at java.base/java.util.Optional.map(Optional.java:265)
        at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.<init>(QueryExecutorMethodInterceptor.java:84)
        at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:332)
        at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:297)
        at org.springframework.data.util.Lazy.getNullable(Lazy.java:211)
        at org.springframework.data.util.Lazy.get(Lazy.java:95)
        at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:300)
        at org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactoryBean.afterPropertiesSet(R2dbcRepositoryFactoryBean.java:143)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1853)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1790)
        ... 70 more
    Caused by: org.springframework.data.mapping.PropertyReferenceException: No property exists found for type Solution!
        at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:94)
        at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:382)
        at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:358)
        at org.springframework.data.mapping.PropertyPath.lambda$from$0(PropertyPath.java:311)
        at java.base/java.util.concurrent.ConcurrentMap.computeIfAbsent(ConcurrentMap.java:330)
        at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:293)
        at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:276)
        at org.springframework.data.repository.query.parser.Part.<init>(Part.java:82)
        at org.springframework.data.repository.query.parser.PartTree$OrPart.lambda$new$0(PartTree.java:251)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
        at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
        at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
        at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
        at org.springframework.data.repository.query.parser.PartTree$OrPart.<init>(PartTree.java:252)
        at org.springframework.data.repository.query.parser.PartTree$Predicate.lambda$new$0(PartTree.java:381)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
        at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
        at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
        at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
        at org.springframework.data.repository.query.parser.PartTree$Predicate.<init>(PartTree.java:382)
        at org.springframework.data.repository.query.parser.PartTree.<init>(PartTree.java:94)
        at org.springframework.data.r2dbc.repository.query.PartTreeR2dbcQuery.<init>(PartTreeR2dbcQuery.java:66)
        ... 94 more

Repo :

    import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor;
    import org.springframework.data.repository.reactive.ReactiveSortingRepository;
    public interface MeetingSolverRepository extends ReactiveSortingRepository<Solution, Long>, ReactiveQueryByExampleExecutor<Solution> {
    }

Service :

    @Slf4j
    @Service
    @RequiredArgsConstructor
    public class MeetingSolverServiceImpl implements MeetingSolverService {
        private final MeetingSolverRepository meetingSolverRepository;
        @Override
        public Flux<Solution> findAll(Solution solution) {
            Example<Solution> example = Example.of(solution);
            return meetingSolverRepository.findAll(example);
        }
    }

Object :

import lombok.Data;
import org.springframework.data.annotation.Id;

@Data
public class Solution {

    @Id
    private Long id;
    private Integer tenant;
    private Long meetingId;
    private Long periodId;
    private Long studentId;
    private Long teacherId;
    private Long roomId;

}

DB config :


import dev.miku.r2dbc.mysql.MySqlConnectionConfiguration;
import dev.miku.r2dbc.mysql.MySqlConnectionFactory;
import io.r2dbc.spi.ConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
import org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager;
import org.springframework.data.r2dbc.core.DatabaseClient;
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
import org.springframework.lang.NonNull;
import org.springframework.transaction.ReactiveTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.reactive.TransactionalOperator;

@Configuration
@EnableR2dbcRepositories
@EnableTransactionManagement
public class R2DBCConfiguration extends AbstractR2dbcConfiguration {

    @Value("${spring.datasource.host}") private String host;
    @Value("${spring.datasource.port}") private Integer port;
    @Value("${spring.datasource.schema-name}") private String database;
    @Value("${spring.datasource.username}") private String username;
    @Value("${spring.datasource.password}") private String password;

    @Bean
    @NonNull
    @Override
    public ConnectionFactory connectionFactory() {
        return MySqlConnectionFactory.from(
                MySqlConnectionConfiguration.builder()
                        .host(host)
                        .port(port)
                        .user(username)
                        .password(password)
                        .database(database)
                        .build());
    }

    @Bean({"r2dbcDatabaseClient"})
    @Primary
    public DatabaseClient databaseClient() {
        return DatabaseClient.create(connectionFactory());
    }

    @Bean
    public ReactiveTransactionManager reactiveTransactionManager(ConnectionFactory connectionFactory) {
        return new R2dbcTransactionManager(connectionFactory);
    }

    @Bean
    public TransactionalOperator transactionalOperator(ReactiveTransactionManager reactiveTransactionManager) {
        return TransactionalOperator.create(reactiveTransactionManager);
    }
}

It looks like it's checking the solution object for the exists property and count and so on. I'm assuming I'm missing something in my setup?

Any help would be greatly appreciated. Thank you.


Solution

  • I don't think Spring Data R2DBC org.springframework.data.r2dbc.repository.support.SimpleR2dbcRepository implements the ReactiveQueryByExampleExecutor

    Unlike its non-reactive counterpart SimpleJpaRepository in Spring Data JPA that implements the non-reactive QueryByExampleExecutor

    Extending your repository interfaces using ReactiveQueryByExampleExecutor interface and expecting Springboot to auto-configure a ReactiveQueryByExampleExecutor implementation will NOT work (as of Spring Data R2DBC 1.0) and Spring Data will assume the unimplemented methods from ReactiveQueryByExampleExecutor are entity property queries hence the exception.

    The solutions may be either

    a) Implement your own ReactiveQueryByExampleExecutor based on your requirements,

    b) switch to Spring Data Mongo if it is an option, or

    c) wait for the community to implement a solution. I believe there is an open issue in Github for this. Good luck!