Skip to content

Commit 154f7e0

Browse files
committed
Issue #182 - Pageable.unpaged() find queries fail
Properly handle `Pageable.unpaged()` requests while keeping AWS lazy-list return semantic accessing the resulting page
1 parent ff118ec commit 154f7e0

15 files changed

+747
-48
lines changed

Diff for: pom.xml

+19-27
Original file line numberDiff line numberDiff line change
@@ -391,33 +391,25 @@
391391
<configLocation>checkstyle.xml</configLocation>
392392
</configuration>
393393
</plugin>
394-
<plugin>
395-
<groupId>org.codehaus.mojo</groupId>
396-
<artifactId>findbugs-maven-plugin</artifactId>
397-
<version>${findbugs.version}</version>
398-
<configuration>
399-
<!-- 2.5.2
400-
Enables analysis which takes more memory but finds more bugs.
401-
If you run out of memory, changes the value of the effort element
402-
to 'Low'.
403-
-->
404-
<effort>Max</effort>
405-
<!-- Reports all bugs (other values are medium and max) -->
406-
<threshold>Max</threshold>
407-
<!-- Produces XML report
408-
<xmlOutput>true</xmlOutput>
409-
<findbugsXmlOutputDirectory>${project.build.directory}/findbugs</findbugsXmlOutputDirectory>
410-
-->
411-
</configuration>
412-
<executions>
413-
<execution>
414-
<phase>compile</phase>
415-
<goals>
416-
<goal>check</goal>
417-
</goals>
418-
</execution>
419-
</executions>
420-
</plugin>
394+
<plugin>
395+
<groupId>com.github.spotbugs</groupId>
396+
<artifactId>spotbugs-maven-plugin</artifactId>
397+
<version>3.1.5</version>
398+
<configuration>
399+
<effort>Max</effort>
400+
<!-- Reports all bugs (other values are medium and max) -->
401+
<threshold>Max</threshold>
402+
<!-- Produces XML report <xmlOutput>true</xmlOutput> -->
403+
</configuration>
404+
<executions>
405+
<execution>
406+
<phase>verify</phase>
407+
<goals>
408+
<goal>check</goal>
409+
</goals>
410+
</execution>
411+
</executions>
412+
</plugin>
421413
<plugin>
422414
<groupId>org.apache.maven.plugins</groupId>
423415
<artifactId>maven-release-plugin</artifactId>

Diff for: src/changes/changes.xml

+3
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@
6767
<action dev="SimY4" issue="183" type="fix" date="2018-07-16">
6868
Match hibernate validator dependency version with Spring Boot 2 dependency.
6969
</action>
70+
<action dev="derjust" issue="181" type="fix" date="">
71+
`Pageable.unpaged()` find queries fail
72+
</action>
7073
</release>
7174
<release version="5.0.2" date="2018-03-05" description="Maintenance release">
7275
<action dev="vitolimandibhrata" issue="40" type="add" date="2017-01-07">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/**
2+
* Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
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+
* http://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.socialsignin.spring.data.dynamodb.domain;
17+
18+
import java.util.Iterator;
19+
import java.util.List;
20+
import java.util.function.Function;
21+
import java.util.stream.Collectors;
22+
23+
import org.springframework.data.domain.Page;
24+
import org.springframework.data.domain.Pageable;
25+
import org.springframework.data.domain.Sort;
26+
import org.springframework.lang.NonNull;
27+
import org.springframework.lang.Nullable;
28+
import org.springframework.util.Assert;
29+
30+
/**
31+
* {@link Page} implementation that uses only the methods from the
32+
* {@link Iterable} interface thus the lazy list from the AWS SDK used as result
33+
* set can be properly used
34+
*
35+
* @param <T>
36+
* The type of the list's elements
37+
*/
38+
public class UnpagedPageImpl<T> implements Page<T> {
39+
40+
private final List<T> content;
41+
private final Pageable pageable;
42+
private final long total;
43+
44+
public UnpagedPageImpl(@NonNull List<T> content, long total) {
45+
46+
Assert.notNull(content, "content must not be null!");
47+
48+
this.pageable = Pageable.unpaged();
49+
this.content = content;
50+
this.total = total;
51+
}
52+
53+
@Override
54+
public int getNumber() {
55+
return 0;
56+
}
57+
58+
@Override
59+
public int getSize() {
60+
return getNumberOfElements();
61+
}
62+
63+
@Override
64+
public int getNumberOfElements() {
65+
if (total > Integer.MAX_VALUE) {
66+
return Integer.MAX_VALUE;
67+
} else {
68+
return (int) total;
69+
}
70+
}
71+
72+
@Override
73+
public Sort getSort() {
74+
return pageable.getSort();
75+
}
76+
77+
@Override
78+
public boolean isFirst() {
79+
return true;
80+
}
81+
82+
@Override
83+
public boolean isLast() {
84+
return true;
85+
}
86+
87+
@Override
88+
public boolean hasNext() {
89+
return false;
90+
}
91+
92+
@Override
93+
public boolean hasPrevious() {
94+
return false;
95+
}
96+
97+
@Override
98+
@Nullable
99+
public Pageable nextPageable() {
100+
return null;
101+
}
102+
103+
@Override
104+
@Nullable
105+
public Pageable previousPageable() {
106+
return null;
107+
}
108+
109+
@Override
110+
public Iterator<T> iterator() {
111+
return this.content.iterator();
112+
}
113+
114+
@Override
115+
public int getTotalPages() {
116+
return 1;
117+
}
118+
119+
@Override
120+
public long getTotalElements() {
121+
return this.total;
122+
}
123+
124+
@Override
125+
public <U> UnpagedPageImpl<U> map(Function<? super T, ? extends U> converter) {
126+
List<U> convertedContent = this.content.stream().map(converter).collect(Collectors.toList());
127+
128+
return new UnpagedPageImpl<>(convertedContent, this.total);
129+
}
130+
131+
@Override
132+
public List<T> getContent() {
133+
return content;
134+
}
135+
136+
@Override
137+
public boolean hasContent() {
138+
return total > 0;
139+
}
140+
141+
/*
142+
* (non-Javadoc)
143+
*
144+
* @see java.lang.Object#toString()
145+
*/
146+
@Override
147+
public String toString() {
148+
149+
String contentType = "UNKNOWN";
150+
151+
if (this.total > 0) {
152+
contentType = iterator().getClass().getName();
153+
}
154+
155+
return String.format("Page %s of %d containing %s instances", getNumber() + 1, getTotalPages(), contentType);
156+
}
157+
158+
/*
159+
* (non-Javadoc)
160+
*
161+
* @see java.lang.Object#equals(java.lang.Object)
162+
*/
163+
@Override
164+
public boolean equals(/* @Nullable */ Object obj) {
165+
166+
if (this == obj) {
167+
return true;
168+
}
169+
170+
if (!(obj instanceof UnpagedPageImpl<?>)) {
171+
return false;
172+
}
173+
174+
UnpagedPageImpl<?> that = (UnpagedPageImpl<?>) obj;
175+
176+
return this.total == that.total && this.pageable.equals(that.pageable) && this.content.equals(that.content);
177+
}
178+
179+
/*
180+
* (non-Javadoc)
181+
*
182+
* @see java.lang.Object#hashCode()
183+
*/
184+
@Override
185+
public int hashCode() {
186+
187+
int result = 17;
188+
189+
result += 31 * (int) (total ^ total >>> 32);
190+
result += 31 * pageable.hashCode();
191+
result += 31 * content.hashCode();
192+
193+
return result;
194+
}
195+
}

Diff for: src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQuery.java

+22-9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
1919
import org.socialsignin.spring.data.dynamodb.core.DynamoDBOperations;
20+
import org.socialsignin.spring.data.dynamodb.domain.UnpagedPageImpl;
2021
import org.socialsignin.spring.data.dynamodb.exception.BatchDeleteException;
2122
import org.socialsignin.spring.data.dynamodb.query.Query;
2223
import org.socialsignin.spring.data.dynamodb.utils.ExceptionHandler;
@@ -31,6 +32,7 @@
3132
import org.springframework.data.repository.query.RepositoryQuery;
3233

3334
import java.util.ArrayList;
35+
import java.util.Collections;
3436
import java.util.Iterator;
3537
import java.util.List;
3638

@@ -163,23 +165,34 @@ public Object execute(AbstractDynamoDBQuery<T, ID> dynamoDBQuery, Object[] value
163165
private Page<T> createPage(List<T> allResults, Pageable pageable, AbstractDynamoDBQuery<T, ID> dynamoDBQuery,
164166
Object[] values) {
165167

168+
// Get the result = this list might be a lazy list
166169
Iterator<T> iterator = allResults.iterator();
167-
if (pageable.getOffset() > 0) {
170+
171+
// Check if the pageable request is 'beyond' the result set
172+
if (!pageable.isUnpaged() && pageable.getOffset() > 0) {
168173
long processedCount = scanThroughResults(iterator, pageable.getOffset());
169-
if (processedCount < pageable.getOffset())
170-
return new PageImpl<>(new ArrayList<T>());
174+
if (processedCount < pageable.getOffset()) {
175+
return new PageImpl<>(Collections.emptyList());
176+
}
171177
}
172-
List<T> results = readPageOfResultsRestrictMaxResultsIfNecessary(iterator, pageable.getPageSize());
173178

179+
// Then Count the result set size
174180
Query<Long> countQuery = dynamoDBQuery.doCreateCountQueryWithPermissions(values, true);
175181
long count = countQuery.getSingleResult();
176182

177-
if (getResultsRestrictionIfApplicable() != null) {
178-
count = Math.min(count, getResultsRestrictionIfApplicable());
179-
}
180-
181-
return new PageImpl<>(results, pageable, count);
183+
// Finally wrap the result in a page -
184+
if (!pageable.isUnpaged()) {
185+
// either seek to the proper part of the result set
186+
if (getResultsRestrictionIfApplicable() != null) {
187+
count = Math.min(count, getResultsRestrictionIfApplicable());
188+
}
182189

190+
List<T> results = readPageOfResultsRestrictMaxResultsIfNecessary(iterator, pageable.getPageSize());
191+
return new PageImpl<>(results, pageable, count);
192+
} else {
193+
// or treat the whole (lazy) list as the result page if it's unpaged
194+
return new UnpagedPageImpl<>(allResults, count);
195+
}
183196
}
184197
}
185198

Diff for: src/site/resources/spring-dynamodb-1.0.xsd

+17
Original file line numberDiff line numberDiff line change
@@ -1 +1,18 @@
1+
<!--
2+
3+
Copyright © 2018 spring-data-dynamodb (https://github.com/derjust/spring-data-dynamodb)
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
17+
-->
118
../../main/resources/org/socialsignin/spring/data/dynamodb/repository/config/spring-dynamodb-1.0.xsd

0 commit comments

Comments
 (0)