Skip to content

Commit 7d044c2

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 aba0d17 commit 7d044c2

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.*;
@@ -40,6 +41,7 @@
4041
* @author Thomas Darimont
4142
* @author Sebastian Staudt
4243
* @author Jens Schauder
44+
* @author Mark Paluch
4345
*/
4446
@SuppressWarnings("serial")
4547
@RunWith(MockitoJUnitRunner.class)
@@ -142,6 +144,36 @@ public void complexSpecificationsShouldBeSerializable() {
142144
assertThat(transferredSpecification).isNotNull();
143145
}
144146

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

147179
@Override

0 commit comments

Comments
 (0)