Skip to content

Commit e20eb59

Browse files
committed
Polishing.
Signed-off-by: Artemiy Chereshnevvv <[email protected]> New hasNext implementation. Signed-off-by: Artemiy Chereshnevvv <[email protected]> Polishing Signed-off-by: Artemiy Chereshnevvv <[email protected]> Remove unused nullable annotation and unused params. Signed-off-by: Artemiy Chereshnevvv <[email protected]> Use already created methods. Signed-off-by: Artemiy Chereshnevvv <[email protected]>
1 parent 3f737be commit e20eb59

File tree

4 files changed

+146
-117
lines changed

4 files changed

+146
-117
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java

Lines changed: 10 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,15 @@
2020
import java.sql.ResultSet;
2121
import java.util.ArrayList;
2222
import java.util.Collection;
23-
import java.util.LinkedHashMap;
2423
import java.util.List;
25-
import java.util.Map;
2624
import java.util.function.Function;
27-
import java.util.function.IntFunction;
2825
import java.util.function.LongSupplier;
2926
import java.util.function.Supplier;
3027

28+
import org.jspecify.annotations.NonNull;
3129
import org.jspecify.annotations.Nullable;
3230
import org.springframework.core.convert.converter.Converter;
33-
import org.springframework.data.domain.KeysetScrollPosition;
3431
import org.springframework.data.domain.Limit;
35-
import org.springframework.data.domain.OffsetScrollPosition;
3632
import org.springframework.data.domain.Pageable;
3733
import org.springframework.data.domain.ScrollPosition;
3834
import org.springframework.data.domain.Slice;
@@ -41,12 +37,11 @@
4137
import org.springframework.data.domain.Window;
4238
import org.springframework.data.jdbc.core.JdbcAggregateOperations;
4339
import org.springframework.data.jdbc.core.convert.JdbcConverter;
44-
import org.springframework.data.mapping.PersistentPropertyAccessor;
40+
import org.springframework.data.jdbc.repository.support.ScrollDelegate;
4541
import org.springframework.data.relational.core.conversion.RelationalConverter;
4642
import org.springframework.data.relational.core.dialect.Dialect;
4743
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
4844
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
49-
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
5045
import org.springframework.data.relational.repository.query.RelationalEntityMetadata;
5146
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
5247
import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor;
@@ -169,7 +164,7 @@ private Sort getDynamicSort(RelationalParameterAccessor accessor) {
169164

170165
@Override
171166
@Nullable
172-
public Object execute(Object[] values) {
167+
public Object execute(@Nullable Object[] values) {
173168

174169
RelationalParametersParameterAccessor accessor = new RelationalParametersParameterAccessor(getQueryMethod(),
175170
values);
@@ -205,20 +200,20 @@ private JdbcQueryExecution<?> getQueryExecution(ResultProcessor processor,
205200

206201
if (getQueryMethod().isScrollQuery()) {
207202
// noinspection unchecked
208-
return new ScrollQueryExecution<>((JdbcQueryExecution<Collection<Object>>) queryExecution,
209-
accessor.getScrollPosition(), this.tree.getMaxResults(), tree.getSort(), tree.getResultLimit(),
203+
return new ScrollQueryExecution<>((JdbcQueryExecution<@NonNull Collection<Object>>) queryExecution,
204+
accessor.getScrollPosition(), tree.getSort(), tree.getResultLimit(),
210205
getQueryMethod().getEntityInformation().getTableEntity());
211206
}
212207

213208
if (getQueryMethod().isSliceQuery()) {
214209
// noinspection unchecked
215-
return new SliceQueryExecution<>((JdbcQueryExecution<Collection<Object>>) queryExecution, accessor.getPageable());
210+
return new SliceQueryExecution<>((JdbcQueryExecution<@NonNull Collection<Object>>) queryExecution, accessor.getPageable());
216211
}
217212

218213
if (getQueryMethod().isPageQuery()) {
219214

220215
// noinspection unchecked
221-
return new PageQueryExecution<>((JdbcQueryExecution<Collection<Object>>) queryExecution, accessor.getPageable(),
216+
return new PageQueryExecution<>((JdbcQueryExecution<@NonNull Collection<Object>>) queryExecution, accessor.getPageable(),
222217
() -> {
223218

224219
RelationalEntityMetadata<?> entityMetadata = getQueryMethod().getEntityInformation();
@@ -243,12 +238,8 @@ private JdbcQueryExecution<?> getQueryExecution(ResultProcessor processor,
243238
}
244239

245240
ParametrizedQuery createQuery(RelationalParametersParameterAccessor accessor, ReturnedType returnedType) {
241+
JdbcQueryCreator queryCreator = new JdbcQueryCreator(tree, converter, dialect, getQueryMethod(), accessor, returnedType);
246242

247-
RelationalEntityMetadata<?> entityMetadata = getQueryMethod().getEntityInformation();
248-
249-
JdbcQueryCreator queryCreator = new JdbcQueryCreator(context, tree, converter, dialect, entityMetadata, accessor,
250-
getQueryMethod().isSliceQuery(), returnedType, this.getQueryMethod().lookupLockAnnotation(),
251-
getQueryMethod().isScrollQuery());
252243
return queryCreator.createQuery(getDynamicSort(accessor));
253244
}
254245

@@ -284,16 +275,14 @@ private JdbcQueryExecution<?> getJdbcQueryExecution(@Nullable ResultSetExtractor
284275
static class ScrollQueryExecution<T> implements JdbcQueryExecution<Window<T>> {
285276
private final JdbcQueryExecution<? extends Collection<T>> delegate;
286277
private final @Nullable ScrollPosition position;
287-
private final @Nullable Integer maxResults;
288278
private final Sort sort;
289279
private final Limit limit;
290280
private final RelationalPersistentEntity<?> tableEntity;
291281

292282
ScrollQueryExecution(JdbcQueryExecution<? extends Collection<T>> delegate, @Nullable ScrollPosition position,
293-
@Nullable Integer maxResults, Sort sort, Limit limit, RelationalPersistentEntity<?> tableEntity) {
283+
Sort sort, Limit limit, RelationalPersistentEntity<?> tableEntity) {
294284
this.delegate = delegate;
295285
this.position = position;
296-
this.maxResults = maxResults;
297286
this.sort = sort;
298287
this.limit = limit;
299288
this.tableEntity = tableEntity;
@@ -303,65 +292,7 @@ static class ScrollQueryExecution<T> implements JdbcQueryExecution<Window<T>> {
303292
public @Nullable Window<T> execute(String query, SqlParameterSource parameter) {
304293
Collection<T> result = delegate.execute(query, parameter);
305294

306-
List<T> resultList = result instanceof List ? (List<T>) result : new ArrayList<>(result);
307-
IntFunction<? extends ScrollPosition> positionFunction = null;
308-
if (position instanceof OffsetScrollPosition)
309-
positionFunction = ((OffsetScrollPosition) position).positionFunction();
310-
311-
if (position instanceof KeysetScrollPosition) {
312-
Map<String, Object> keys = ((KeysetScrollPosition) position).getKeys();
313-
List<String> orders = new ArrayList<>(keys.keySet());
314-
315-
if (orders.isEmpty())
316-
orders = sort.get().map(Sort.Order::getProperty).toList();
317-
318-
List<RelationalPersistentProperty> properties = new ArrayList<>();
319-
for (String propertyName : orders) {
320-
RelationalPersistentProperty prop = tableEntity.getPersistentProperty(propertyName);
321-
if (prop == null)
322-
continue;
323-
324-
properties.add(prop);
325-
}
326-
327-
final Map<String, Object> resultKeys = extractKeys(resultList, properties);
328-
positionFunction = (ignoredI) -> ScrollPosition.of(resultKeys, ((KeysetScrollPosition) position).getDirection());
329-
}
330-
331-
if (positionFunction == null)
332-
throw new UnsupportedOperationException("Not supported scroll type.");
333-
334-
boolean hasNext;
335-
if (maxResults != null)
336-
hasNext = resultList.size() >= maxResults;
337-
else if (limit.isLimited())
338-
hasNext = resultList.size() >= limit.max();
339-
else
340-
hasNext = !resultList.isEmpty();
341-
342-
return Window.from(resultList, positionFunction, hasNext);
343-
}
344-
345-
private Map<String, Object> extractKeys(List<T> resultList, List<RelationalPersistentProperty> properties) {
346-
if (resultList.isEmpty())
347-
return Map.of();
348-
349-
Map<String, Object> result = new LinkedHashMap<>();
350-
351-
T last = resultList.get(resultList.size() - 1);
352-
PersistentPropertyAccessor<T> accessor = tableEntity.getPropertyAccessor(last);
353-
354-
for (RelationalPersistentProperty property : properties) {
355-
String propertyName = property.getName();
356-
Object propertyValue = accessor.getProperty(property);
357-
358-
if (propertyValue == null)
359-
continue;
360-
361-
result.put(propertyName, propertyValue);
362-
}
363-
364-
return result;
295+
return ScrollDelegate.scroll(result, position, limit, sort, tableEntity);
365296
}
366297
}
367298

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StatementFactory.java

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.Collection;
2121
import java.util.List;
2222
import java.util.Map;
23-
import java.util.Set;
2423
import java.util.function.Predicate;
2524
import java.util.stream.Collectors;
2625

@@ -235,25 +234,6 @@ public String build(MapSqlParameterSource parameterSource) {
235234
return SqlRenderer.create(renderContextFactory.createRenderContext()).render(select);
236235
}
237236

238-
Sort applyScrollOrderBy(Sort sort, @Nullable ScrollPosition scrollPosition) {
239-
if (!(scrollPosition instanceof KeysetScrollPosition) || scrollPosition.isInitial())
240-
return sort;
241-
242-
Set<String> sortedProperties = sort.get().map(Sort.Order::getProperty).collect(Collectors.toSet());
243-
244-
Set<String> keys = ((KeysetScrollPosition) scrollPosition).getKeys().keySet();
245-
246-
Set<String> notSortedProperties
247-
= keys.stream().filter(it -> !sortedProperties.contains(it)).collect(Collectors.toSet());
248-
249-
if (notSortedProperties.isEmpty())
250-
return sort;
251-
252-
Sort.Direction defaultSort = sort.get().map(Sort.Order::getDirection).findAny().orElse(Sort.DEFAULT_DIRECTION);
253-
254-
return sort.and(Sort.by(defaultSort, notSortedProperties.toArray(new String[0])));
255-
}
256-
257237
Criteria applyScrollCriteria(@Nullable ScrollPosition position, Sort sort) {
258238
if (!(position instanceof KeysetScrollPosition keyset) || position.isInitial() || keyset.getKeys().isEmpty()) {
259239
return Criteria.empty();
@@ -303,11 +283,8 @@ Criteria buildKeysetCriteria(List<String> columns, List<Object> values, boolean
303283

304284
SelectBuilder.SelectOrdered applyOrderBy(Sort sort, RelationalPersistentEntity<?> entity, Table table,
305285
SelectBuilder.SelectOrdered selectOrdered) {
306-
307-
Sort resultSort = applyScrollOrderBy(sort, scrollPosition);
308-
309-
return resultSort.isSorted() ? //
310-
selectOrdered.orderBy(queryMapper.getMappedSort(table, resultSort, entity)) //
286+
return sort.isSorted() ? //
287+
selectOrdered.orderBy(queryMapper.getMappedSort(table, sort, entity)) //
311288
: selectOrdered;
312289
}
313290

@@ -340,9 +317,16 @@ SelectBuilder.SelectWhere applyLimitAndOffset(SelectBuilder.SelectLimitOffset li
340317
.offset(pageable.getOffset());
341318
}
342319

320+
if (mode == Mode.SCROLL) {
321+
int pageSize = limit.isLimited() ? limit.max() : 0;
322+
323+
limitOffsetBuilder = limitOffsetBuilder
324+
.limit(pageSize + 1);
325+
}
326+
343327
if (mode == Mode.SCROLL && scrollPosition != null && scrollPosition instanceof OffsetScrollPosition
344328
&& !scrollPosition.isInitial()) {
345-
int pageSize = limit.isLimited() ? limit.max() : Integer.MAX_VALUE;
329+
int pageSize = limit.isLimited() ? limit.max() : 0;
346330

347331
limitOffsetBuilder = limitOffsetBuilder.offset(((OffsetScrollPosition) scrollPosition).getOffset() * pageSize);
348332
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/ScrollDelegate.java

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,26 @@
1515
*/
1616
package org.springframework.data.jdbc.repository.support;
1717

18+
import java.util.ArrayList;
19+
import java.util.Collection;
20+
import java.util.HashMap;
21+
import java.util.HashSet;
1822
import java.util.List;
23+
import java.util.Map;
24+
import java.util.Set;
1925
import java.util.function.Function;
2026
import java.util.function.IntFunction;
27+
import java.util.stream.Collectors;
2128

29+
import org.springframework.data.domain.KeysetScrollPosition;
30+
import org.springframework.data.domain.Limit;
2231
import org.springframework.data.domain.OffsetScrollPosition;
2332
import org.springframework.data.domain.ScrollPosition;
33+
import org.springframework.data.domain.Sort;
2434
import org.springframework.data.domain.Window;
35+
import org.springframework.data.mapping.PersistentPropertyAccessor;
36+
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
37+
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
2538
import org.springframework.data.relational.core.query.Query;
2639
import org.springframework.util.Assert;
2740

@@ -32,6 +45,75 @@
3245
* @since 3.1.4
3346
*/
3447
public class ScrollDelegate {
48+
/**
49+
* Run the {@link String} and return a scroll {@link Window}
50+
*
51+
* @param resultCollection must not be {@literal null}
52+
* @param scrollPosition must not be {@literal null}
53+
* @return the scroll {@link Window}
54+
*/
55+
public static <T> Window<T> scroll(Collection<T> resultCollection, ScrollPosition scrollPosition, Limit limit, Sort sort, RelationalPersistentEntity<?> entity) {
56+
Assert.notNull(scrollPosition, "ScrollPosition must not be null");
57+
Assert.notNull(resultCollection, "ResultCollection must not be null");
58+
59+
List<T> resultList = resultCollection instanceof List<T> ? (List<T>) resultCollection : new ArrayList<>(resultCollection);
60+
if (scrollPosition instanceof OffsetScrollPosition offset) {
61+
return createWindow(resultList, limit, offset.positionFunction());
62+
}
63+
64+
if (scrollPosition instanceof KeysetScrollPosition keyset) {
65+
List<Sort.Order> orderList = sort.get().toList();
66+
List<String> sortProperties = orderList.stream()
67+
.map(Sort.Order::getProperty)
68+
.toList();
69+
70+
Map<String, Object> keys = keyset.getKeys();
71+
if (!keys.isEmpty()) {
72+
Set<String> keySet = keys.keySet();
73+
Set<String> properties = new HashSet<>(sortProperties);
74+
75+
Set<String> missingKeys = keySet.stream()
76+
.filter(key -> !properties.contains(key))
77+
.collect(Collectors.toSet());
78+
79+
if (!missingKeys.isEmpty())
80+
throw new IllegalStateException(
81+
"Keyset keys are not present in sort properties: " + missingKeys +
82+
". Available sort properties: " + properties
83+
);
84+
}
85+
86+
Set<RelationalPersistentProperty> properties = new HashSet<>();
87+
for (String propertyName : keys.keySet()) {
88+
RelationalPersistentProperty prop = entity.getPersistentProperty(propertyName);
89+
if (prop == null)
90+
throw new IllegalArgumentException(
91+
"Unknown property: " + propertyName + ". Available properties: " + String.join(", ", sortProperties)
92+
);
93+
94+
properties.add(prop);
95+
}
96+
97+
if (properties.isEmpty()) {
98+
for (String sortProperty : sortProperties) {
99+
RelationalPersistentProperty prop = entity.getPersistentProperty(sortProperty);
100+
if (prop == null)
101+
throw new IllegalArgumentException("Unknown property: " + sortProperty + ".");
102+
103+
properties.add(prop);
104+
}
105+
}
106+
107+
Map<String, Object> resultKeys = extractKeys(resultList, properties, limit, entity);
108+
ScrollPosition.Direction direction = keyset.getDirection();
109+
110+
IntFunction<? extends ScrollPosition> positionFunction = ignoredI -> ScrollPosition.of(resultKeys, direction);
111+
112+
return createWindow(resultList, limit, positionFunction);
113+
}
114+
115+
throw new UnsupportedOperationException("ScrollPosition " + scrollPosition + " not supported");
116+
}
35117

36118
/**
37119
* Run the {@link Query} and return a scroll {@link Window}.
@@ -40,7 +122,6 @@ public class ScrollDelegate {
40122
* @param scrollPosition must not be {@literal null}.
41123
* @return the scroll {@link Window}.
42124
*/
43-
@SuppressWarnings("unchecked")
44125
public static <T> Window<T> scroll(Query query, Function<Query, List<T>> queryFunction,
45126
ScrollPosition scrollPosition) {
46127

@@ -60,6 +141,39 @@ public static <T> Window<T> scroll(Query query, Function<Query, List<T>> queryFu
60141
throw new UnsupportedOperationException("ScrollPosition " + scrollPosition + " not supported");
61142
}
62143

144+
private static <T> Map<String, Object> extractKeys(List<T> resultList, Set<RelationalPersistentProperty> properties, Limit limit, RelationalPersistentEntity<?> entity) {
145+
if (resultList.isEmpty())
146+
return Map.of();
147+
148+
int pageSize = limit.isUnlimited() ? 0 : limit.max();
149+
List<T> limitedList = getFirst(pageSize, resultList);
150+
151+
if (limitedList.isEmpty())
152+
return Map.of();
153+
154+
T last = limitedList.get(limitedList.size() - 1);
155+
PersistentPropertyAccessor<T> accessor = entity.getPropertyAccessor(last);
156+
157+
Map<String, Object> result = new HashMap<>();
158+
for (RelationalPersistentProperty property : properties) {
159+
String propertyName = property.getName();
160+
Object propertyValue = accessor.getProperty(property);
161+
162+
if (propertyValue == null)
163+
throw new IllegalStateException("Unexpected null value for keyset property '" + propertyName + "'. Keyset pagination requires all sort properties to be non-nullable");
164+
165+
result.put(propertyName, propertyValue);
166+
}
167+
168+
return result;
169+
}
170+
171+
private static <T> Window<T> createWindow(List<T> result, Limit limit, IntFunction<? extends ScrollPosition> positionFunction) {
172+
int limitSize = limit.isLimited() ? limit.max() : 0;
173+
174+
return createWindow(result, limitSize, positionFunction);
175+
}
176+
63177
private static <T> Window<T> createWindow(List<T> result, int limit,
64178
IntFunction<? extends ScrollPosition> positionFunction) {
65179
return Window.from(getFirst(limit, result), positionFunction, hasMoreElements(result, limit));

0 commit comments

Comments
 (0)