Skip to content

Commit 2798624

Browse files
committed
Reintroduce Specification.where(Specification)
Reintroduce the overload to improve the migration path for users upgrading to Spring Data JPA 4.0 and to restore the intuitive fluent API. Closes #3992
1 parent 48bc6aa commit 2798624

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
* @author Jens Schauder
4949
* @author Daniel Shuy
5050
* @author Sergey Rukin
51+
* @author Heeeun Cho
5152
*/
5253
@FunctionalInterface
5354
public interface Specification<T> extends Serializable {
@@ -78,6 +79,21 @@ static <T> Specification<T> where(PredicateSpecification<T> spec) {
7879
return (root, update, criteriaBuilder) -> spec.toPredicate(root, criteriaBuilder);
7980
}
8081

82+
/**
83+
* Creates a {@link Specification} from the given {@link Specification}. This is a factory method for fluent composition.
84+
*
85+
* @param <T> the type of the {@link Root} the resulting {@literal Specification} operates on.
86+
* @param spec must not be {@literal null}.
87+
* @return the given specification.
88+
* @since 4.1
89+
*/
90+
static <T> Specification<T> where(Specification<T> spec) {
91+
92+
Assert.notNull(spec, "Specification must not be null");
93+
94+
return spec;
95+
}
96+
8197
/**
8298
* ANDs the given {@link Specification} to the current one.
8399
*

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
* @author Jens Schauder
4343
* @author Mark Paluch
4444
* @author Daniel Shuy
45+
* @author Heeeun Cho
4546
*/
4647
@SuppressWarnings({ "unchecked", "deprecation" })
4748
@ExtendWith(MockitoExtension.class)
@@ -137,6 +138,36 @@ void notWithNullPredicate() {
137138
verify(builder).disjunction();
138139
}
139140

141+
@Test // GH-3992
142+
void whereWithSpecificationReturnsSameSpecification() {
143+
144+
Specification<Object> originalSpec = (r, q, cb) -> predicate;
145+
Specification<Object> wrappedSpec = Specification.where(originalSpec);
146+
147+
assertThat(wrappedSpec).isSameAs(originalSpec);
148+
}
149+
150+
@Test // GH-3992
151+
void whereWithSpecificationSupportsFluentComposition() {
152+
153+
Specification<Object> firstSpec = (r, q, cb) -> predicate;
154+
Specification<Object> secondSpec = (r, q, cb) -> predicate;
155+
156+
Specification<Object> composedSpec = Specification.where(firstSpec).and(secondSpec);
157+
158+
assertThat(composedSpec).isNotNull();
159+
composedSpec.toPredicate(root, query, builder);
160+
verify(builder).and(predicate, predicate);
161+
}
162+
163+
@Test // GH-3992
164+
void whereWithNullSpecificationThrowsException() {
165+
166+
assertThatThrownBy(() -> Specification.where((Specification<Object>) null))
167+
.isInstanceOf(IllegalArgumentException.class)
168+
.hasMessage("Specification must not be null");
169+
}
170+
140171
static class SerializableSpecification implements Serializable, Specification<Object> {
141172

142173
@Override

0 commit comments

Comments
 (0)