Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4718c45

Browse files
committed
Introduce public ReactivePageableExecutionUtils variant of pageable utils.
Closes #3209
1 parent 771347b commit 4718c45

File tree

2 files changed

+181
-0
lines changed

2 files changed

+181
-0
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2021-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.support;
17+
18+
import reactor.core.publisher.Mono;
19+
20+
import java.util.List;
21+
22+
import org.springframework.data.domain.Page;
23+
import org.springframework.data.domain.PageImpl;
24+
import org.springframework.data.domain.Pageable;
25+
import org.springframework.util.Assert;
26+
27+
/**
28+
* Support for query execution using {@link Pageable}. Using {@link ReactivePageableExecutionUtils} assumes that data
29+
* queries are cheaper than {@code COUNT} queries and so some cases can take advantage of optimizations.
30+
*
31+
* @author Mark Paluch
32+
* @since 3.5
33+
*/
34+
public abstract class ReactivePageableExecutionUtils {
35+
36+
private ReactivePageableExecutionUtils() {}
37+
38+
/**
39+
* Constructs a {@link Page} based on the given {@code content}, {@link Pageable} and {@link Mono} applying
40+
* optimizations. The construction of {@link Page} omits a count query if the total can be determined based on the
41+
* result size and {@link Pageable}.
42+
*
43+
* @param content must not be {@literal null}.
44+
* @param pageable must not be {@literal null}.
45+
* @param totalSupplier must not be {@literal null}.
46+
* @return the {@link Page}.
47+
*/
48+
public static <T> Mono<Page<T>> getPage(List<T> content, Pageable pageable, Mono<Long> totalSupplier) {
49+
50+
Assert.notNull(content, "Content must not be null");
51+
Assert.notNull(pageable, "Pageable must not be null");
52+
Assert.notNull(totalSupplier, "TotalSupplier must not be null");
53+
54+
if (pageable.isUnpaged() || pageable.getOffset() == 0) {
55+
56+
if (pageable.isUnpaged() || pageable.getPageSize() > content.size()) {
57+
return Mono.just(new PageImpl<>(content, pageable, content.size()));
58+
}
59+
60+
return totalSupplier.map(total -> new PageImpl<>(content, pageable, total));
61+
}
62+
63+
if (!content.isEmpty() && pageable.getPageSize() > content.size()) {
64+
return Mono.just(new PageImpl<>(content, pageable, pageable.getOffset() + content.size()));
65+
}
66+
67+
return totalSupplier.map(total -> new PageImpl<>(content, pageable, total));
68+
}
69+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright 2020-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.support;
17+
18+
import static java.util.Collections.*;
19+
import static org.assertj.core.api.Assertions.*;
20+
import static org.mockito.Mockito.*;
21+
22+
import reactor.core.publisher.Mono;
23+
24+
import java.util.Arrays;
25+
26+
import org.junit.jupiter.api.Test;
27+
import org.junit.jupiter.api.extension.ExtendWith;
28+
import org.mockito.junit.jupiter.MockitoExtension;
29+
30+
import org.springframework.data.domain.PageRequest;
31+
import org.springframework.data.domain.Pageable;
32+
33+
/**
34+
* Unit tests for {@link ReactivePageableExecutionUtils}.
35+
*
36+
* @author Mark Paluch
37+
*/
38+
@SuppressWarnings("unchecked")
39+
@ExtendWith(MockitoExtension.class)
40+
class ReactivePageableExecutionUtilsUnitTests {
41+
42+
@Test // DATAMCNS-884, GH-3209
43+
void firstPageRequestIsLessThanOneFullPageDoesNotRequireTotal() {
44+
45+
Mono<Long> totalSupplierMock = mock(Mono.class);
46+
var page = ReactivePageableExecutionUtils.getPage(Arrays.asList(1, 2, 3), PageRequest.of(0, 10), totalSupplierMock)
47+
.block();
48+
49+
assertThat(page).contains(1, 2, 3);
50+
assertThat(page.getTotalElements()).isEqualTo(3L);
51+
verifyNoInteractions(totalSupplierMock);
52+
}
53+
54+
@Test // DATAMCNS-884, GH-3209
55+
void noPageableRequestDoesNotRequireTotal() {
56+
57+
Mono<Long> totalSupplierMock = mock(Mono.class);
58+
var page = ReactivePageableExecutionUtils.getPage(Arrays.asList(1, 2, 3), Pageable.unpaged(), totalSupplierMock)
59+
.block();
60+
61+
assertThat(page).contains(1, 2, 3);
62+
assertThat(page.getTotalElements()).isEqualTo(3L);
63+
64+
verifyNoInteractions(totalSupplierMock);
65+
}
66+
67+
@Test // DATAMCNS-884, GH-3209
68+
void subsequentPageRequestIsLessThanOneFullPageDoesNotRequireTotal() {
69+
70+
Mono<Long> totalSupplierMock = mock(Mono.class);
71+
var page = ReactivePageableExecutionUtils.getPage(Arrays.asList(1, 2, 3), PageRequest.of(5, 10), totalSupplierMock)
72+
.block();
73+
74+
assertThat(page).contains(1, 2, 3);
75+
assertThat(page.getTotalElements()).isEqualTo(53L);
76+
77+
verifyNoInteractions(totalSupplierMock);
78+
}
79+
80+
@Test // DATAMCNS-884, GH-3209
81+
void firstPageRequestHitsUpperBoundRequiresTotal() {
82+
83+
Mono<Long> totalSupplierMock = Mono.just(4L);
84+
85+
var page = ReactivePageableExecutionUtils.getPage(Arrays.asList(1, 2, 3), PageRequest.of(0, 3), totalSupplierMock)
86+
.block();
87+
88+
assertThat(page).contains(1, 2, 3);
89+
assertThat(page.getTotalElements()).isEqualTo(4L);
90+
}
91+
92+
@Test // DATAMCNS-884, GH-3209
93+
void subsequentPageRequestHitsUpperBoundRequiresTotal() {
94+
95+
Mono<Long> totalSupplierMock = Mono.just(7L);
96+
97+
var page = ReactivePageableExecutionUtils.getPage(Arrays.asList(1, 2, 3), PageRequest.of(1, 3), totalSupplierMock)
98+
.block();
99+
100+
assertThat(page).contains(1, 2, 3);
101+
assertThat(page.getTotalElements()).isEqualTo(7L);
102+
}
103+
104+
@Test // DATAMCNS-884, GH-3209
105+
void subsequentPageRequestWithoutResultRequiresRequireTotal() {
106+
107+
Mono<Long> totalSupplierMock = Mono.just(7L);
108+
var page = ReactivePageableExecutionUtils.getPage(emptyList(), PageRequest.of(5, 10), totalSupplierMock).block();
109+
110+
assertThat(page.getTotalElements()).isEqualTo(7L);
111+
}
112+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /