Skip to content

Commit ab4dd9c

Browse files
committed
Consider Specification order in composition.
We now consider the ordering of left-hand-side and right-hand-side arguments when composing specifications. The primary aspect is consistency so that predicates appear in the actual SQL query in the order they were combined. Most SQL databases tend to reorder the criteria according to the most useful query execution plan. Only special cases tend to follow deferred evaluation when using OR combination. Resolves #2146.
1 parent ae4368f commit ab4dd9c

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

src/main/java/org/springframework/data/jpa/domain/SpecificationComposition.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* @author Sebastian Staudt
3131
* @author Oliver Gierke
3232
* @author Jens Schauder
33+
* @author Mark Paluch
3334
* @see Specification
3435
* @since 2.2
3536
*/
@@ -44,8 +45,8 @@ static <T> Specification<T> composed(@Nullable Specification<T> lhs, @Nullable S
4445

4546
return (root, query, builder) -> {
4647

47-
Predicate otherPredicate = toPredicate(lhs, root, query, builder);
48-
Predicate thisPredicate = toPredicate(rhs, root, query, builder);
48+
Predicate thisPredicate = toPredicate(lhs, root, query, builder);
49+
Predicate otherPredicate = toPredicate(rhs, root, query, builder);
4950

5051
if (thisPredicate == null) {
5152
return otherPredicate;

src/test/java/org/springframework/data/jpa/domain/SpecificationUnitTests.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.jpa.domain;
1717

1818
import static org.assertj.core.api.Assertions.*;
19+
import static org.mockito.Mockito.*;
1920
import static org.springframework.data.jpa.domain.Specification.*;
2021
import static org.springframework.data.jpa.domain.Specification.not;
2122
import static org.springframework.util.SerializationUtils.*;
@@ -42,6 +43,7 @@
4243
* @author Thomas Darimont
4344
* @author Sebastian Staudt
4445
* @author Jens Schauder
46+
* @author Mark Paluch
4547
*/
4648
@SuppressWarnings("serial")
4749
@ExtendWith(MockitoExtension.class)
@@ -145,6 +147,36 @@ void complexSpecificationsShouldBeSerializable() {
145147
assertThat(transferredSpecification).isNotNull();
146148
}
147149

150+
@Test // #2146
151+
void andCombinesSpecificationsInOrder() {
152+
153+
Predicate firstPredicate = mock(Predicate.class);
154+
Predicate secondPredicate = mock(Predicate.class);
155+
156+
Specification<Object> first = ((root1, query1, criteriaBuilder) -> firstPredicate);
157+
158+
Specification<Object> second = ((root1, query1, criteriaBuilder) -> secondPredicate);
159+
160+
first.and(second).toPredicate(root, query, builder);
161+
162+
verify(builder).and(firstPredicate, secondPredicate);
163+
}
164+
165+
@Test // #2146
166+
void orCombinesSpecificationsInOrder() {
167+
168+
Predicate firstPredicate = mock(Predicate.class);
169+
Predicate secondPredicate = mock(Predicate.class);
170+
171+
Specification<Object> first = ((root1, query1, criteriaBuilder) -> firstPredicate);
172+
173+
Specification<Object> second = ((root1, query1, criteriaBuilder) -> secondPredicate);
174+
175+
first.or(second).toPredicate(root, query, builder);
176+
177+
verify(builder).or(firstPredicate, secondPredicate);
178+
}
179+
148180
static class SerializableSpecification implements Serializable, Specification<Object> {
149181

150182
@Override

0 commit comments

Comments
 (0)