Skip to content

Commit 472a359

Browse files
committed
Revise PredicateSpecification for improved reuse.
Accept From instead of Root and apply the PredicateSpecification type parameter to From's target type. Dropping the type reduces our assumptions, it allows also for improved PredicateSpecification reuse without requiring a specific design choice of whether the PredicateSpecification applies to Root directly or a different From (Join, Embeddable, …). Closes #4035
1 parent 7a66d65 commit 472a359

File tree

3 files changed

+20
-14
lines changed

3 files changed

+20
-14
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/domain/PredicateSpecification.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
package org.springframework.data.jpa.domain;
1717

1818
import jakarta.persistence.criteria.CriteriaBuilder;
19+
import jakarta.persistence.criteria.From;
1920
import jakarta.persistence.criteria.Predicate;
20-
import jakarta.persistence.criteria.Root;
2121

2222
import java.io.Serializable;
2323
import java.util.Arrays;
@@ -34,12 +34,16 @@
3434
* <p>
3535
* Specifications can be composed into higher order functions from other specifications using
3636
* {@link #and(PredicateSpecification)}, {@link #or(PredicateSpecification)} or factory methods such as
37-
* {@link #allOf(Iterable)}.
37+
* {@link #allOf(Iterable)} with reduced type interference of the query source type.
38+
* <p>
39+
* PredicateSpecifications are building blocks for composition and do not express their type opinion towards a specific
40+
* entity source or join source type for improved reuse.
3841
* <p>
3942
* Composition considers whether one or more specifications contribute to the overall predicate by returning a
4043
* {@link Predicate} or {@literal null}. Specifications returning {@literal null}, such as {@link #unrestricted()}, are
4144
* considered to not contribute to the overall predicate, and their result is not considered in the final predicate.
4245
*
46+
* @param <T> the type of the {@link From From target} to which the specification is applied.
4347
* @author Mark Paluch
4448
* @author Peter Aisher
4549
* @since 4.0
@@ -57,17 +61,17 @@ public interface PredicateSpecification<T> extends Serializable {
5761
* not(unrestricted()) // equivalent to `unrestricted()`
5862
* </pre>
5963
*
60-
* @param <T> the type of the {@link Root} the resulting {@literal PredicateSpecification} operates on.
64+
* @param <T> the type of the {@link From} the resulting {@literal PredicateSpecification} operates on.
6165
* @return guaranteed to be not {@literal null}.
6266
*/
6367
static <T> PredicateSpecification<T> unrestricted() {
64-
return (root, builder) -> null;
68+
return (from, builder) -> null;
6569
}
6670

6771
/**
6872
* Simple static factory method to add some syntactic sugar around a {@literal PredicateSpecification}.
6973
*
70-
* @param <T> the type of the {@link Root} the resulting {@literal PredicateSpecification} operates on.
74+
* @param <T> the type of the {@link From} the resulting {@literal PredicateSpecification} operates on.
7175
* @param spec must not be {@literal null}.
7276
* @return guaranteed to be not {@literal null}.
7377
* @since 2.0
@@ -112,17 +116,17 @@ default PredicateSpecification<T> or(PredicateSpecification<T> other) {
112116
/**
113117
* Negates the given {@link PredicateSpecification}.
114118
*
115-
* @param <T> the type of the {@link Root} the resulting {@literal PredicateSpecification} operates on.
119+
* @param <T> the type of the {@link From} the resulting {@literal PredicateSpecification} operates on.
116120
* @param spec can be {@literal null}.
117121
* @return guaranteed to be not {@literal null}.
118122
*/
119123
static <T> PredicateSpecification<T> not(PredicateSpecification<T> spec) {
120124

121125
Assert.notNull(spec, "Specification must not be null");
122126

123-
return (root, builder) -> {
127+
return (from, builder) -> {
124128

125-
Predicate predicate = spec.toPredicate(root, builder);
129+
Predicate predicate = spec.toPredicate(from, builder);
126130
return predicate != null ? builder.not(predicate) : null;
127131
};
128132
}
@@ -187,13 +191,13 @@ static <T> PredicateSpecification<T> anyOf(Iterable<PredicateSpecification<T>> s
187191

188192
/**
189193
* Creates a WHERE clause for a query of the referenced entity in form of a {@link Predicate} for the given
190-
* {@link Root} and {@link CriteriaBuilder}.
194+
* {@link From} and {@link CriteriaBuilder}.
191195
*
192-
* @param root must not be {@literal null}.
196+
* @param from must not be {@literal null}.
193197
* @param criteriaBuilder must not be {@literal null}.
194198
* @return a {@link Predicate}, may be {@literal null}.
195199
*/
196200
@Nullable
197-
Predicate toPredicate(Root<T> root, CriteriaBuilder criteriaBuilder);
201+
Predicate toPredicate(From<?, T> from, CriteriaBuilder criteriaBuilder);
198202

199203
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import jakarta.persistence.criteria.CriteriaDelete;
2020
import jakarta.persistence.criteria.CriteriaQuery;
2121
import jakarta.persistence.criteria.CriteriaUpdate;
22+
import jakarta.persistence.criteria.From;
2223
import jakarta.persistence.criteria.Predicate;
2324
import jakarta.persistence.criteria.Root;
2425

@@ -127,9 +128,9 @@ static <T> PredicateSpecification<T> composed(PredicateSpecification<T> lhs, Pre
127128
};
128129
}
129130

130-
private static <T> @Nullable Predicate toPredicate(@Nullable PredicateSpecification<T> specification, Root<T> root,
131+
private static <T> @Nullable Predicate toPredicate(@Nullable PredicateSpecification<T> specification, From<?, T> from,
131132
CriteriaBuilder builder) {
132-
return specification == null ? null : specification.toPredicate(root, builder);
133+
return specification == null ? null : specification.toPredicate(from, builder);
133134
}
134135

135136
}

spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/PredicateSpecificationUnitTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static org.springframework.util.SerializationUtils.*;
2121

2222
import jakarta.persistence.criteria.CriteriaBuilder;
23+
import jakarta.persistence.criteria.From;
2324
import jakarta.persistence.criteria.Predicate;
2425
import jakarta.persistence.criteria.Root;
2526

@@ -169,7 +170,7 @@ void notWithNullPredicate() {
169170
static class SerializableSpecification implements Serializable, PredicateSpecification<Object> {
170171

171172
@Override
172-
public Predicate toPredicate(Root<Object> root, CriteriaBuilder cb) {
173+
public Predicate toPredicate(From<?, Object> root, CriteriaBuilder cb) {
173174
return null;
174175
}
175176
}

0 commit comments

Comments
 (0)