Как применить разбиение на страницы в реактивных данных Spring?

В Spring Data у нас есть PagingAndSortingRepository, который наследуется от CrudRepository. В реактивных Spring Data у нас есть только ReactiveSortingRepository, который наследуется от ReactiveCrudRepository. Как мы могли сделать нумерацию страниц реактивным способом? Сможем ли мы сделать это в будущем, например, с ReactivePagingAndSortingRepository?


person Steph    schedule 23.09.2017    source источник
comment
Какой вариант использования вы хотите решить? Это массовая обработка данных (постранично) или вы хотите просто получить определенный фрагмент данных?   -  person mp911de    schedule 24.09.2017
comment
второй момент. Я просто хочу управлять веб-сервисом, разбивая данные на страницы, а не извлекая все за один раз   -  person Steph    schedule 25.09.2017


Ответы (7)


Реактивные репозитории Spring Data MongoDB не обеспечивают разбиение на страницы в смысле разбиения по страницам, как это разработано для императивных репозиториев. Императивная разбивка на страницы требует дополнительных сведений при загрузке страницы. Особенно:

  • Количество возвращенных записей для запроса на разбиение на страницы
  • Необязательно, общее количество записей, которые выдает запрос, если количество возвращенных записей равно нулю или соответствует размеру страницы, для расчета общего количества страниц.

Оба аспекта не подходят под понятие эффективного неблокирующего использования ресурсов. Ожидание получения всех записей (для определения первого фрагмента сведений о подкачке) лишит вас огромной части преимуществ, которые вы получаете от реактивного доступа к данным. Кроме того, выполнение запроса подсчета довольно дорого и увеличивает задержку до тех пор, пока вы не сможете обработать данные.

Вы по-прежнему можете получать фрагменты данных самостоятельно, передав Pageable (PageRequest) методам запроса репозитория:

interface ReactivePersonRepository extends Repository<Person, Long> {

  Flux<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable);
}

Spring Data применит разбиение на страницы к запросу, переведя Pageable в LIMIT и OFFSET.

Использованная литература:

person mp911de    schedule 24.09.2017
comment
Как можно восстановить левые страницы на вашем примере? В нереактивном стиле репозитории предоставляют тип Page ‹Person›, который содержит информацию. В вашем примере с Flux информация о странице недоступна. - person zennon; 02.01.2018
comment
Аналогичный вопрос: stackoverflow.com/questions/44478189/ - person zennon; 02.01.2018
comment
Вам нужно будет получить счет и рассчитать оставшиеся страницы оттуда. - person mp911de; 02.01.2018
comment
Есть ли планы добавить эту функциональность во фреймворк? Поддержка разбивки на страницы на основе курсора была бы замечательной. - person zennon; 02.01.2018
comment
Синхронизация результатов и добавление счетчика лишаются многих преимуществ, которые дает вам реактивный. Не совсем уверен, будет ли поддержка на уровне фреймворка иметь смысл для широкой аудитории. - person mp911de; 02.01.2018
comment
И похоже, что Spring Data не предоставляет реактивного PageableHandlerMethodArgumentResolver для разрешения Pageable в контроллере. - person Hantsy; 23.01.2018
comment
@ mp911de Но когда я писал тесты для разбивки на страницы, я не уверен, почему test из countByTitleLike не прошел тестовую проверку env. - person Hantsy; 23.01.2018

import com.thepracticaldeveloper.reactiveweb.domain.Quote;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import reactor.core.publisher.Flux;

public interface QuoteMongoReactiveRepository extends ReactiveCrudRepository<Quote, String> {

    @Query("{ id: { $exists: true }}")
    Flux<Quote> retrieveAllQuotesPaged(final Pageable page);
}

подробнее, вы можете проверить здесь < / а>

person kn3l    schedule 04.05.2018
comment
+1 Ваше решение работает как шарм !!! Я изменил название с помощью findAll @Query ({id: {\ $ exists: true}}) fun findAll (page: Pageable): Flux ‹Цитата› - person CRISTIAN ROMERO MATESANZ; 23.03.2020

Я создал сервис с этим методом для всех, кто, возможно, все еще ищет решение:

@Resource
private UserRepository userRepository; //Extends ReactiveSortingRepository<User, String>

public Mono<Page<User>> findAllUsersPaged(Pageable pageable) {
        return this.userRepository.count()
                .flatMap(userCount -> {
                    return this.userRepository.findAll(pageable.getSort())
                            .buffer(pageable.getPageSize(),(pageable.getPageNumber() + 1))
                            .elementAt(pageable.getPageNumber(), new ArrayList<>())
                            .map(users -> new PageImpl<User>(users, pageable, userCount));
                });
    }
person Sam1am    schedule 11.09.2019
comment
Не найдет ли он все документы на стороне БД? - person shj; 20.01.2020

Я создал другой подход с использованием решения @ kn3l (без использования @Query):

fun findByIdNotNull(page: Pageable): Flux< Quote>

Он создает тот же запрос без использования метода @Query

person CRISTIAN ROMERO MATESANZ    schedule 23.03.2020

Я столкнулся с той же проблемой и в конечном итоге использовал аналогичный подход, как указано выше, но немного изменил код, поскольку я использую Query DSL, следующий пример, если кому-то нужно.

@Repository
public interface PersonRepository extends ReactiveMongoRepository<Person, String>, ReactiveQuerydslPredicateExecutor<Person> {

    default Flux<Person> applyPagination(Flux<Person> persons, Pageable pageable) {
    return persons.buffer(pageable.getPageSize(), (pageable.getPageNumber() + 1))
        .elementAt(pageable.getPageNumber(), new ArrayList<>())
        .flatMapMany(Flux::fromIterable);
    }

}


public Flux<Person> findAll(Pageable pageable, Predicate predicate) {
    return personRepository.applyPagination(personRepository.findAll(predicate), pageable);
}
person user2669657    schedule 03.12.2020

В поисках идеи для реактивных страничных репозиториев я видел решения, которые приводят к ужасному коду котельной пластины, поэтому я закончил с этим (еще не пробовал в реальной жизни, но должен работать нормально, или, может быть, это может быть вдохновением для вашего решения)

Итак ... давайте создадим новый класс панели инструментов с таким методом

    public static 
           <R extends PageableForReactiveMongo<S, K>, S, T, K> Mono<Page<T>>
           pageableForReactiveMongo(Pageable pageable, 
                                             R repository, Class<T> clazzTo) {
        return repository.count()
                .flatMap(c ->
                        repository.findOderByLimitedTo(pageable.getSort(),
                                              pageable.getPageNumber() + 1)
                                .buffer(pageable.getPageSize(), (pageable.getPageNumber() + 1))
                                .elementAt(pageable.getPageNumber(), new ArrayList<>())
                                .map(r -> mapToPage(pageable, c, r, clazzTo))
                );
    }

и потребуется еще что-то вроде этого:

    private static <S, T> Page<T> mapToPage(Pageable pageable, Long userCount, Collection<S> collection, Class<T> clazzTo) {
        return new PageImpl<>(
                collection.stream()
                        .map(r -> mapper.map(r, clazzTo))
                        .collect(Collectors.toList())
                , pageable, userCount);
    }

а затем нам также понадобится абстрактный слой, обертывающий реактивные репозитории

public interface PageableForReactiveMongo<D, K> extends ReactiveMongoRepository<D, K> {
    Flux<D> findOderByLimitedTo(Sort sort, int i);
}

чтобы он появился к весне

@Repository
interface ControllerRepository extends PageableForReactiveMongo<ControllerDocument, String> {
}

И, наконец, используйте его много-много раз вот так

public Mono<Page<Controller>> findAllControllers(Pageable pageable) {
    return getFromPageableForReactiveMongo(pageable, controllerRepository, Controller.class);
}

так может выглядеть ваш код :) скажите, пожалуйста, в порядке ли он или чем-то помог

person Michał Brzeziński    schedule 30.01.2021

person    schedule
comment
Спасибо за ваши усилия. Пожалуйста, добавьте поясняющий текст, описывающий идею вашего решения. - person Markus-Hermann; 23.09.2020
comment
Только кодовые ответы - это ответы низкого качества. - person Taslim Oseni; 23.09.2020