spring-projects / spring-data-commons

Spring Data Commons. Interfaces and code shared between the various datastore specific implementations.
https://spring.io/projects/spring-data
Apache License 2.0
783 stars 676 forks source link

Support Reactive Repositories in Jackson2RepositoryPopulatorFactoryBean [DATACMNS-1133] #1575

Closed spring-projects-issues closed 2 years ago

spring-projects-issues commented 7 years ago

Juergen Zimmermann opened DATACMNS-1133 and commented

We finally migrated our microservices to Spring Cloud Finchley.M1, Spring Boot 2.0.M3, Spring Security 5.0.0.M3, and Spring Data Kay.RC2.All Repos are reactive. However, in our "dev" profile we we are populating a MongoDB database via Jackson2RepositoryPopulatorFactoryBean. Just for this purpose we still have to define (synchroneous) CrudRepositories. Therefore, we would appreciate if Jackson2RepositoryPopulatorFactoryBean could use our ReactiveCrudRepositories


Affects: 2.0 RC2 (Kay)

1 votes, 2 watchers

spring-projects-issues commented 5 years ago

nerg4l commented

Can this be added to the the documentation for Spring Data MongoDB - Section 8.8.3. Repository Populators and Spring Data R2DBC - Section 11.8.3. Repository Populators? It surprised me when I switched from MongoRepository to ReactiveMongoRepository and Jackson2RepositoryPopulatorFactoryBean stopped working all of a sudden

porterwoodward commented 3 years ago

After stepping through the code - and watching the org.springframework.data.repository.support.ReflectionRepositoryInvoker manage to locate the .save method on the reactive mongo repo - but then fail to subscribe to the resulting mono/result it makes sense that the operations are never committed.

    protected RepositoryInvoker createInvoker(RepositoryInformation information, Object repository) {

        if (repository instanceof PagingAndSortingRepository) {
            return new PagingAndSortingRepositoryInvoker((PagingAndSortingRepository<Object, Object>) repository, information,
                    conversionService);
        } else if (repository instanceof CrudRepository) {
            return new CrudRepositoryInvoker((CrudRepository<Object, Object>) repository, information, conversionService);
        } else {
            return new ReflectionRepositoryInvoker(repository, information, conversionService);
        }
    }

Perhaps a check for instanceof ReactiveSortingRepository where the return is a new, sub-class of the ReflectionRepositoryInvoker, ReflectionReactiveRepositoryInvoker - where that invoker calls the terminal method on the Mono<> returned from the save method(s)?

dspitzer commented 2 years ago

This took me by surprise as well and cost me quite a few minutes to figure this out. Seemingly for a very long time the populator just doesn't work with reactive repositories.

rolag-it commented 2 years ago

Hello, I'm working on a fix, see my pull request #2543

nerg4l commented 2 years ago

Related #2558

jndietz commented 2 years ago

We worked around this in a service we use for mock data:

@Configuration
@EnableReactiveMongoRepositories(basePackages = "com.company.api.mock.repository")
@Slf4j
class MongoConfiguration {

    @Value("classpath:personas/*.json")
    Resource[] resources

    @Autowired
    AccountRepository repository

    /**
     *
     * @param objectMapper
     * @return Disposable
     *
     * This is a hacky way to seed mongodb with some data from json files.  Usually we
     * should use Jackson2RepositoryPopulatorFactoryBean, but it doesn't work with reactive
     * mongodb repositories.
     */
    @Bean
    Disposable populator(ObjectMapper objectMapper) {
        Flux.just(resources)
            .flatMap({
                def json = new String(it.inputStream.readAllBytes())
                def accounts = objectMapper.readValue(json, Account[])
                repository.saveAll(accounts.toList())
            })
            .subscribe() // shouldn't need to do this but not sure what the right way is
    }
}
mp911de commented 2 years ago

Superseded by #2558