在Spring Data中,我们有继承自CrudRepository
的PagingAndSortingRepository
。在响应式Spring Data中,我们只有继承自ReactiveCrudRepository
的ReactiveSortingRepository
。
如何以响应式方式实现分页?
未来是否能够使用例如ReactivePagingAndSortingRepository
实现分页呢?
在Spring Data中,我们有继承自CrudRepository
的PagingAndSortingRepository
。在响应式Spring Data中,我们只有继承自ReactiveCrudRepository
的ReactiveSortingRepository
。
如何以响应式方式实现分页?
未来是否能够使用例如ReactivePagingAndSortingRepository
实现分页呢?
响应式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
。 - Hantsyimport 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));
});
}
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);
}
让它由Spring实例化
@Repository
interface ControllerRepository extends PageableForReactiveMongo<ControllerDocument, String> {
}
最后像这样多次使用它
public Mono<Page<Controller>> findAllControllers(Pageable pageable) {
return getFromPageableForReactiveMongo(pageable, controllerRepository, Controller.class);
}
这就是你的代码可能看起来的样子 :) 请告诉我它是否可以,或者对你有所帮助
我使用了@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 Mono<Page<ChatUser>> findByChannelIdPageable(String channelId, Integer page, Integer size) {
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "chatChannels.joinedTime"));
Criteria criteria = new Criteria("chatChannels.chatChannelId").is(channelId);
Query query = new Query().with(pageable);
query.addCriteria(criteria);
Flux<ChatUser> chatUserFlux = reactiveMongoTemplate.find(query, ChatUser.class, "chatUser");
Mono<Long> countMono = reactiveMongoTemplate.count(Query.of(query).limit(-1).skip(-1), ChatUser.class);
return Mono.zip(chatUserFlux.collectList(),countMono).map(tuple2 -> {
return PageableExecutionUtils.getPage(
tuple2.getT1(),
pageable,
() -> tuple2.getT2());
});
}