diff --git a/pom.xml b/pom.xml index 9f675e8b85..b55c08203d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-commons - 2.4.0-SNAPSHOT + 2.4.0-DATACMNS-1820-SNAPSHOT Spring Data Core diff --git a/src/main/java/org/springframework/data/repository/support/PageableExecutionUtils.java b/src/main/java/org/springframework/data/repository/support/PageableExecutionUtils.java index 2ea62f4e69..c8053f5172 100644 --- a/src/main/java/org/springframework/data/repository/support/PageableExecutionUtils.java +++ b/src/main/java/org/springframework/data/repository/support/PageableExecutionUtils.java @@ -19,9 +19,7 @@ import java.util.function.LongSupplier; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; -import org.springframework.util.Assert; /** * Support for query execution using {@link Pageable}. Using {@link PageableExecutionUtils} assumes that data queries @@ -30,8 +28,11 @@ * @author Mark Paluch * @author Oliver Gierke * @author Christoph Strobl + * @author Jens Schauder * @since 1.13 + * @deprecated since 2.4. Use {@link org.springframework.data.support.PageableExecutionUtils} instead */ +@Deprecated public abstract class PageableExecutionUtils { private PageableExecutionUtils() {} @@ -47,24 +48,6 @@ private PageableExecutionUtils() {} * @return the {@link Page}. */ public static Page getPage(List content, Pageable pageable, LongSupplier totalSupplier) { - - Assert.notNull(content, "Content must not be null!"); - Assert.notNull(pageable, "Pageable must not be null!"); - Assert.notNull(totalSupplier, "TotalSupplier must not be null!"); - - if (pageable.isUnpaged() || pageable.getOffset() == 0) { - - if (pageable.isUnpaged() || pageable.getPageSize() > content.size()) { - return new PageImpl<>(content, pageable, content.size()); - } - - return new PageImpl<>(content, pageable, totalSupplier.getAsLong()); - } - - if (content.size() != 0 && pageable.getPageSize() > content.size()) { - return new PageImpl<>(content, pageable, pageable.getOffset() + content.size()); - } - - return new PageImpl<>(content, pageable, totalSupplier.getAsLong()); + return org.springframework.data.support.PageableExecutionUtils.getPage(content, pageable, totalSupplier); } } diff --git a/src/main/java/org/springframework/data/support/PageableExecutionUtils.java b/src/main/java/org/springframework/data/support/PageableExecutionUtils.java new file mode 100644 index 0000000000..90384787c6 --- /dev/null +++ b/src/main/java/org/springframework/data/support/PageableExecutionUtils.java @@ -0,0 +1,71 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.support; + +import java.util.List; +import java.util.function.LongSupplier; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.util.Assert; + +/** + * Support for query execution using {@link Pageable}. Using {@link PageableExecutionUtils} assumes that data queries + * are cheaper than {@code COUNT} queries and so some cases can take advantage of optimizations. + * + * @author Mark Paluch + * @author Oliver Gierke + * @author Christoph Strobl + * @author Jens Schauder + * @since 2.4 + */ +public abstract class PageableExecutionUtils { + + private PageableExecutionUtils() {} + + /** + * Constructs a {@link Page} based on the given {@code content}, {@link Pageable} and {@link Supplier} applying + * optimizations. The construction of {@link Page} omits a count query if the total can be determined based on the + * result size and {@link Pageable}. + * + * @param content must not be {@literal null}. + * @param pageable must not be {@literal null}. + * @param totalSupplier must not be {@literal null}. + * @return the {@link Page}. + */ + public static Page getPage(List content, Pageable pageable, LongSupplier totalSupplier) { + + Assert.notNull(content, "Content must not be null!"); + Assert.notNull(pageable, "Pageable must not be null!"); + Assert.notNull(totalSupplier, "TotalSupplier must not be null!"); + + if (pageable.isUnpaged() || pageable.getOffset() == 0) { + + if (pageable.isUnpaged() || pageable.getPageSize() > content.size()) { + return new PageImpl<>(content, pageable, content.size()); + } + + return new PageImpl<>(content, pageable, totalSupplier.getAsLong()); + } + + if (content.size() != 0 && pageable.getPageSize() > content.size()) { + return new PageImpl<>(content, pageable, pageable.getOffset() + content.size()); + } + + return new PageImpl<>(content, pageable, totalSupplier.getAsLong()); + } +} diff --git a/src/test/java/org/springframework/data/support/PageableExecutionUtilsUnitTests.java b/src/test/java/org/springframework/data/support/PageableExecutionUtilsUnitTests.java new file mode 100755 index 0000000000..9d1a119b43 --- /dev/null +++ b/src/test/java/org/springframework/data/support/PageableExecutionUtilsUnitTests.java @@ -0,0 +1,117 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.support; + +import static java.util.Collections.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Arrays; +import java.util.function.LongSupplier; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +/** + * Unit tests for {@link PageableExecutionUtils}. + * + * @author Mark Paluch + * @author Oliver Gierke + * @author Jens Schauder + */ +@ExtendWith(MockitoExtension.class) +class PageableExecutionUtilsUnitTests { + + @Mock LongSupplier totalSupplierMock; + + @Test // DATAMCNS-884 + void firstPageRequestIsLessThanOneFullPageDoesNotRequireTotal() { + + Page page = PageableExecutionUtils.getPage(Arrays.asList(1, 2, 3), PageRequest.of(0, 10), + totalSupplierMock); + + assertThat(page).contains(1, 2, 3); + assertThat(page.getTotalElements()).isEqualTo(3L); + verifyNoInteractions(totalSupplierMock); + } + + @Test // DATAMCNS-884 + void noPageableRequestDoesNotRequireTotal() { + + Page page = PageableExecutionUtils.getPage(Arrays.asList(1, 2, 3), Pageable.unpaged(), totalSupplierMock); + + assertThat(page).contains(1, 2, 3); + assertThat(page.getTotalElements()).isEqualTo(3L); + + verifyNoInteractions(totalSupplierMock); + } + + @Test // DATAMCNS-884 + void subsequentPageRequestIsLessThanOneFullPageDoesNotRequireTotal() { + + Page page = PageableExecutionUtils.getPage(Arrays.asList(1, 2, 3), PageRequest.of(5, 10), + totalSupplierMock); + + assertThat(page).contains(1, 2, 3); + assertThat(page.getTotalElements()).isEqualTo(53L); + + verifyNoInteractions(totalSupplierMock); + } + + @Test // DATAMCNS-884 + void firstPageRequestHitsUpperBoundRequiresTotal() { + + doReturn(4L).when(totalSupplierMock).getAsLong(); + + Page page = PageableExecutionUtils.getPage(Arrays.asList(1, 2, 3), PageRequest.of(0, 3), + totalSupplierMock); + + assertThat(page).contains(1, 2, 3); + assertThat(page.getTotalElements()).isEqualTo(4L); + + verify(totalSupplierMock).getAsLong(); + } + + @Test // DATAMCNS-884 + void subsequentPageRequestHitsUpperBoundRequiresTotal() { + + doReturn(7L).when(totalSupplierMock).getAsLong(); + + Page page = PageableExecutionUtils.getPage(Arrays.asList(1, 2, 3), PageRequest.of(1, 3), + totalSupplierMock); + + assertThat(page).contains(1, 2, 3); + assertThat(page.getTotalElements()).isEqualTo(7L); + + verify(totalSupplierMock).getAsLong(); + } + + @Test // DATAMCNS-884 + void subsequentPageRequestWithoutResultRequiresRequireTotal() { + + doReturn(7L).when(totalSupplierMock).getAsLong(); + Page page = PageableExecutionUtils.getPage(emptyList(), PageRequest.of(5, 10), totalSupplierMock); + + assertThat(page.getTotalElements()).isEqualTo(7L); + + verify(totalSupplierMock).getAsLong(); + } +}