Skip to content

Commit 978c0b3

Browse files
committed
fix: fix query generation for three or more keys
1 parent 34c5c85 commit 978c0b3

File tree

2 files changed

+51
-24
lines changed

2 files changed

+51
-24
lines changed

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

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -249,39 +249,42 @@ Criteria applyScrollCriteria(Criteria criteria, @Nullable ScrollPosition positio
249249
List<String> columns = new ArrayList<>(keys.keySet());
250250
List<Object> values = new ArrayList<>(keys.values());
251251

252+
if (columns.isEmpty() || values.isEmpty())
253+
return criteria;
254+
252255
Map<String, Sort.Direction> directions =
253256
sort.stream().collect(Collectors.toMap(Sort.Order::getProperty, Sort.Order::getDirection));
254257

255-
String primary = columns.get(0);
256-
Object primaryValue = values.get(0);
258+
Sort.Direction dir = directions.getOrDefault(columns.get(0), Sort.DEFAULT_DIRECTION);
257259

258-
Criteria primaryCompare;
259-
Sort.Direction dir = directions.getOrDefault(primary, Sort.DEFAULT_DIRECTION);
260+
Criteria scroll = buildKeysetCriteria(columns, values, keyset.scrollsForward(), dir);
260261

261-
if (keyset.scrollsForward() ^ dir.isDescending()) {
262-
primaryCompare = columns.size() != 1 ? Criteria.where(primary).greaterThanOrEquals(primaryValue) : Criteria.where(primary).greaterThan(primaryValue);
263-
} else {
264-
primaryCompare = columns.size() != 1 ? Criteria.where(primary).lessThanOrEquals(primaryValue) : Criteria.where(primary).lessThan(primaryValue);
265-
}
262+
return criteria.and(scroll);
263+
}
266264

267-
if (columns.size() == 1)
268-
return criteria.and(primaryCompare);
265+
Criteria buildKeysetCriteria(List<String> columns, List<Object> values, boolean isForward, Sort.Direction dir) {
266+
if (columns.isEmpty())
267+
return Criteria.empty();
269268

270-
Criteria tupleCompare = null;
271-
for (int i = 0; i < columns.size(); i++) {
272-
Criteria orCriteria;
273-
String col = columns.get(i);
274-
Object val = values.get(i);
269+
String column = columns.get(0);
270+
Object value = values.get(0);
275271

276-
Sort.Direction colDir = directions.getOrDefault(col, dir);
277-
boolean asc = keyset.scrollsForward() ? colDir.isAscending() : colDir.isDescending();
272+
boolean isAscending = isForward ^ dir.isDescending();
278273

279-
orCriteria = asc ? Criteria.where(col).greaterThan(val) : Criteria.where(col).lessThan(val);
274+
Criteria gte = isAscending
275+
? Criteria.where(column).greaterThanOrEquals(value)
276+
: Criteria.where(column).lessThanOrEquals(value);
280277

281-
tupleCompare = (tupleCompare == null) ? orCriteria : tupleCompare.or(orCriteria);
282-
}
278+
Criteria gt = isAscending
279+
? Criteria.where(column).greaterThan(value)
280+
: Criteria.where(column).lessThan(value);
281+
282+
if (columns.size() == 1)
283+
return gt;
284+
285+
Criteria nested = buildKeysetCriteria(columns.subList(1, columns.size()), values.subList(1, values.size()), isForward, dir);
283286

284-
return criteria.and(primaryCompare).and(tupleCompare);
287+
return gte.and(gt.or(nested));
285288
}
286289

287290
SelectBuilder.SelectOrdered applyOrderBy(Sort sort, RelationalPersistentEntity<?> entity, Table table,

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -744,13 +744,37 @@ void createsQueryForCountProjection() throws Exception {
744744
void createQueryForKeysetTwoKeys() throws Exception {
745745
JdbcQueryMethod queryMethod = getQueryMethod("findFirst2ByOrderByIdIdAsc", ScrollPosition.class);
746746
PartTreeJdbcQuery jdbcQuery = createQuery(queryMethod);
747-
ParametrizedQuery query = jdbcQuery.createQuery((getAccessor(queryMethod, new Object[]{ScrollPosition.of(new LinkedHashMap<>(Map.of("idId", 1, "firstName", "John")), ScrollPosition.Direction.FORWARD)})), returnedType);
747+
Map<String, Object> values = new LinkedHashMap<>() {{
748+
put("idId", 1);
749+
put("firstName", "John");
750+
}};
751+
752+
ParametrizedQuery query = jdbcQuery.createQuery((getAccessor(queryMethod, new Object[]{ScrollPosition.of(values, ScrollPosition.Direction.FORWARD)})), returnedType);
748753

749754
QueryAssert.assertThat(query).containsQuotedAliasedColumns(columns)
750-
.contains(" WHERE (" + TABLE + ".\"ID\" >= :id) AND (" + TABLE + ".\"ID\" > :id1 OR (" + TABLE + ".\"FIRST_NAME\" > :first_name").hasBindValue("id", 1L)
755+
.contains(" WHERE (" + TABLE + ".\"ID\" >= :id AND (" + TABLE + ".\"ID\" > :id1 OR (" + TABLE + ".\"FIRST_NAME\" > :first_name").hasBindValue("id", 1L)
751756
.hasBindValue("first_name", "John");
752757
}
753758

759+
@Test // PR-2149
760+
void createQueryForKeysetThreeKeys() throws Exception {
761+
JdbcQueryMethod queryMethod = getQueryMethod("findFirst2ByOrderByIdIdAsc", ScrollPosition.class);
762+
PartTreeJdbcQuery jdbcQuery = createQuery(queryMethod);
763+
Map<String, Object> values = new LinkedHashMap<>() {{
764+
put("idId", 1);
765+
put("firstName", "John");
766+
put("age", 18);
767+
}};
768+
769+
ParametrizedQuery query = jdbcQuery.createQuery((getAccessor(queryMethod, new Object[]{ScrollPosition.of(values, ScrollPosition.Direction.FORWARD)})), returnedType);
770+
771+
QueryAssert.assertThat(query).containsQuotedAliasedColumns(columns)
772+
.contains("WHERE (" + TABLE + ".\"ID\" >= :id AND (" + TABLE + ".\"ID\" > :id1 OR (" + TABLE + ".\"FIRST_NAME\" >= :first_name AND (" + TABLE + ".\"FIRST_NAME\" > :first_name1 OR (" + TABLE + ".\"AGE\" > :age)")
773+
.hasBindValue("id", 1L)
774+
.hasBindValue("first_name", "John")
775+
.hasBindValue("age", 18);
776+
}
777+
754778
private PartTreeJdbcQuery createQuery(JdbcQueryMethod queryMethod) {
755779
return new PartTreeJdbcQuery(mappingContext, queryMethod, JdbcH2Dialect.INSTANCE, converter,
756780
mock(NamedParameterJdbcOperations.class), mock(RowMapper.class));

0 commit comments

Comments
 (0)