Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2e489cb

Browse files
committedJun 8, 2023
Avoid duplicate sort orders through Keyset scrolling.
We now avoid adding sort properties if they are already specified by Sort. Closes #2996
1 parent 90de693 commit 2e489cb

File tree

2 files changed

+91
-2
lines changed

2 files changed

+91
-2
lines changed
 

‎spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/KeysetScrollSpecification.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import jakarta.persistence.criteria.Predicate;
2323
import jakarta.persistence.criteria.Root;
2424

25+
import java.util.ArrayList;
26+
import java.util.Collection;
2527
import java.util.List;
2628

2729
import org.springframework.data.domain.KeysetScrollPosition;
@@ -61,11 +63,21 @@ public static Sort createSort(KeysetScrollPosition position, Sort sort, JpaEntit
6163

6264
KeysetScrollDelegate delegate = KeysetScrollDelegate.of(position.getDirection());
6365

66+
Collection<String> sortById;
6467
Sort sortToUse;
6568
if (entity.hasCompositeId()) {
66-
sortToUse = sort.and(Sort.by(entity.getIdAttributeNames().toArray(new String[0])));
69+
sortById = new ArrayList<>(entity.getIdAttributeNames());
6770
} else {
68-
sortToUse = sort.and(Sort.by(entity.getRequiredIdAttribute().getName()));
71+
sortById = new ArrayList<>(1);
72+
sortById.add(entity.getRequiredIdAttribute().getName());
73+
}
74+
75+
sort.forEach(it -> sortById.remove(it.getProperty()));
76+
77+
if (sortById.isEmpty()) {
78+
sortToUse = sort;
79+
} else {
80+
sortToUse = sort.and(Sort.by(sortById.toArray(new String[0])));
6981
}
7082

7183
return delegate.getSortOrders(sortToUse);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2023 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.jpa.repository.query;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
20+
import jakarta.persistence.EntityManager;
21+
import jakarta.persistence.PersistenceContext;
22+
23+
import org.junit.jupiter.api.Test;
24+
import org.junit.jupiter.api.extension.ExtendWith;
25+
import org.springframework.data.domain.ScrollPosition;
26+
import org.springframework.data.domain.Sort;
27+
import org.springframework.data.domain.Sort.Order;
28+
import org.springframework.data.jpa.domain.sample.SampleWithIdClass;
29+
import org.springframework.data.jpa.domain.sample.User;
30+
import org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation;
31+
import org.springframework.test.context.ContextConfiguration;
32+
import org.springframework.test.context.junit.jupiter.SpringExtension;
33+
import org.springframework.transaction.annotation.Transactional;
34+
35+
/**
36+
* Unit tests for {@link KeysetScrollSpecification}.
37+
*
38+
* @author Mark Paluch
39+
*/
40+
@ExtendWith(SpringExtension.class)
41+
@ContextConfiguration({ "classpath:infrastructure.xml" })
42+
@Transactional
43+
class KeysetScrollSpecificationUnitTests {
44+
45+
@PersistenceContext EntityManager em;
46+
47+
@Test // GH-2996
48+
void shouldAddIdentifierToSort() {
49+
50+
Sort sort = KeysetScrollSpecification.createSort(ScrollPosition.keyset(), Sort.by("firstname"),
51+
new JpaMetamodelEntityInformation<>(User.class, em.getMetamodel(),
52+
em.getEntityManagerFactory().getPersistenceUnitUtil()));
53+
54+
assertThat(sort).extracting(Order::getProperty).containsExactly("firstname", "id");
55+
}
56+
57+
@Test // GH-2996
58+
void shouldAddCompositeIdentifierToSort() {
59+
60+
Sort sort = KeysetScrollSpecification.createSort(ScrollPosition.keyset(), Sort.by("first", "firstname"),
61+
new JpaMetamodelEntityInformation<>(SampleWithIdClass.class, em.getMetamodel(),
62+
em.getEntityManagerFactory().getPersistenceUnitUtil()));
63+
64+
assertThat(sort).extracting(Order::getProperty).containsExactly("first", "firstname", "second");
65+
}
66+
67+
@Test // GH-2996
68+
void shouldSkipExistingIdentifiersInSort() {
69+
70+
Sort sort = KeysetScrollSpecification.createSort(ScrollPosition.keyset(), Sort.by("id", "firstname"),
71+
new JpaMetamodelEntityInformation<>(User.class, em.getMetamodel(),
72+
em.getEntityManagerFactory().getPersistenceUnitUtil()));
73+
74+
assertThat(sort).extracting(Order::getProperty).containsExactly("id", "firstname");
75+
}
76+
77+
}

0 commit comments

Comments
 (0)
Please sign in to comment.