В Spring Data у нас есть PagingAndSortingRepository
, который наследуется от CrudRepository
. В реактивных Spring Data у нас есть только ReactiveSortingRepository
, который наследуется от ReactiveCrudRepository
. Как мы могли сделать нумерацию страниц реактивным способом? Сможем ли мы сделать это в будущем, например, с ReactivePagingAndSortingRepository
?
Как применить разбиение на страницы в реактивных данных Spring?
Ответы (7)
Реактивные репозитории Spring Data MongoDB не обеспечивают разбиение на страницы в смысле разбиения по страницам, как это разработано для императивных репозиториев. Императивная разбивка на страницы требует дополнительных сведений при загрузке страницы. Особенно:
- Количество возвращенных записей для запроса на разбиение на страницы
- Необязательно, общее количество записей, которые выдает запрос, если количество возвращенных записей равно нулю или соответствует размеру страницы, для расчета общего количества страниц.
Оба аспекта не подходят под понятие эффективного неблокирующего использования ресурсов. Ожидание получения всех записей (для определения первого фрагмента сведений о подкачке) лишит вас огромной части преимуществ, которые вы получаете от реактивного доступа к данным. Кроме того, выполнение запроса подсчета довольно дорого и увеличивает задержку до тех пор, пока вы не сможете обработать данные.
Вы по-прежнему можете получать фрагменты данных самостоятельно, передав Pageable
(PageRequest
) методам запроса репозитория:
interface ReactivePersonRepository extends Repository<Person, Long> {
Flux<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable);
}
Spring Data применит разбиение на страницы к запросу, переведя Pageable
в LIMIT
и OFFSET
.
Использованная литература:
PageableHandlerMethodArgumentResolver
для разрешения Pageable
в контроллере.
- person Hantsy; 23.01.2018
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);
}
подробнее, вы можете проверить здесь < / а>
Я создал сервис с этим методом для всех, кто, возможно, все еще ищет решение:
@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));
});
}
Я создал другой подход с использованием решения @ kn3l (без использования @Query):
fun findByIdNotNull(page: Pageable): Flux< Quote>
Он создает тот же запрос без использования метода @Query
Я столкнулся с той же проблемой и в конечном итоге использовал аналогичный подход, как указано выше, но немного изменил код, поскольку я использую 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);
}
В поисках идеи для реактивных страничных репозиториев я видел решения, которые приводят к ужасному коду котельной пластины, поэтому я закончил с этим (еще не пробовал в реальной жизни, но должен работать нормально, или, может быть, это может быть вдохновением для вашего решения)
Итак ... давайте создадим новый класс панели инструментов с таким методом
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);
}
так может выглядеть ваш код :) скажите, пожалуйста, в порядке ли он или чем-то помог