Skip to content

Commit 5111811

Browse files
committed
Add FluentQuery support to QueryByExampleExecutor and ReactiveQueryByExampleExecutor.
FluentQuery allows extending a query specification initially defined by a Example probe or a Querydsl Predicate and fetching the actual result through a functional programming model: interface PersonRepository extends QuerydslPredicateExecutor<Person> { <S extends T, R> R findBy(Predicate predicate, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction); } PersonRepository repo = …; List<PersonProjection> result = repo.findBy(QPerson.person.name.eq("Walter"), q -> q.sort(Sort.by("lastname")).as(PersonProjection.class).all()); Closes: #2228 Original pull request: #2421.
1 parent f1f0893 commit 5111811

File tree

5 files changed

+303
-0
lines changed

5 files changed

+303
-0
lines changed

src/main/java/org/springframework/data/querydsl/QuerydslPredicateExecutor.java

+13
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
package org.springframework.data.querydsl;
1717

1818
import java.util.Optional;
19+
import java.util.function.Function;
1920

2021
import org.springframework.data.domain.Page;
2122
import org.springframework.data.domain.Pageable;
2223
import org.springframework.data.domain.Sort;
24+
import org.springframework.data.repository.query.FluentQuery;
2325

2426
import com.querydsl.core.types.OrderSpecifier;
2527
import com.querydsl.core.types.Predicate;
@@ -108,4 +110,15 @@ public interface QuerydslPredicateExecutor<T> {
108110
* @return {@literal true} if the data store contains elements that match the given {@link Predicate}.
109111
*/
110112
boolean exists(Predicate predicate);
113+
114+
/**
115+
* Returns entities matching the given {@link Predicate} applying the {@link Function queryFunction} that defines the
116+
* query and its result type.
117+
*
118+
* @param predicate must not be {@literal null}.
119+
* @param queryFunction the query function defining projection, sorting, and the result type
120+
* @return all entities matching the given {@link Predicate}.
121+
* @since 2.6
122+
*/
123+
<S extends T, R> R findBy(Predicate predicate, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction);
111124
}

src/main/java/org/springframework/data/querydsl/ReactiveQuerydslPredicateExecutor.java

+17
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@
1818
import reactor.core.publisher.Flux;
1919
import reactor.core.publisher.Mono;
2020

21+
import java.util.function.Function;
22+
23+
import org.reactivestreams.Publisher;
24+
2125
import org.springframework.data.domain.Sort;
26+
import org.springframework.data.repository.query.FluentQuery;
2227

2328
import com.querydsl.core.types.OrderSpecifier;
2429
import com.querydsl.core.types.Predicate;
@@ -127,4 +132,16 @@ public interface ReactiveQuerydslPredicateExecutor<T> {
127132
* @throws IllegalArgumentException if the required parameter is {@literal null}.
128133
*/
129134
Mono<Boolean> exists(Predicate predicate);
135+
136+
/**
137+
* Returns entities matching the given {@link Predicate} applying the {@link Function queryFunction} that defines the
138+
* query and its result type.
139+
*
140+
* @param predicate must not be {@literal null}.
141+
* @param queryFunction the query function defining projection, sorting, and the result type
142+
* @return all entities matching the given {@link Predicate}.
143+
* @since 2.6
144+
*/
145+
<S extends T, R, P extends Publisher<R>> P findBy(Predicate predicate,
146+
Function<FluentQuery.ReactiveFluentQuery<S>, P> queryFunction);
130147
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
/*
2+
* Copyright 2021 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.repository.query;
17+
18+
import reactor.core.publisher.Flux;
19+
import reactor.core.publisher.Mono;
20+
21+
import java.util.Arrays;
22+
import java.util.Collection;
23+
import java.util.List;
24+
import java.util.Optional;
25+
import java.util.stream.Stream;
26+
27+
import org.springframework.data.domain.Page;
28+
import org.springframework.data.domain.Pageable;
29+
import org.springframework.data.domain.Sort;
30+
import org.springframework.lang.Nullable;
31+
32+
/**
33+
* Fluent interface to define and run a query along with projection and sorting and. Instances of {@link FluentQuery}
34+
* are immutable.
35+
*
36+
* @author Mark Paluch
37+
* @since 2.6
38+
*/
39+
public interface FluentQuery<T> {
40+
41+
/**
42+
* Define the sort order.
43+
*
44+
* @param sort must not be {@code null}.
45+
* @return a new instance of {@link FluentQuery}.
46+
* @throws IllegalArgumentException if resultType is {@code null}.
47+
*/
48+
FluentQuery<T> sortBy(Sort sort);
49+
50+
/**
51+
* Define the target type the result should be mapped to. Skip this step if you are only interested in the original
52+
* domain type.
53+
*
54+
* @param resultType must not be {@code null}.
55+
* @param <R> result type.
56+
* @return a new instance of {@link FluentQuery}.
57+
* @throws IllegalArgumentException if resultType is {@code null}.
58+
*/
59+
<R> FluentQuery<R> as(Class<R> resultType);
60+
61+
/**
62+
* Define which properties or property paths to include in the query.
63+
*
64+
* @param properties must not be {@code null}.
65+
* @return a new instance of {@link FluentQuery}.
66+
* @throws IllegalArgumentException if fields is {@code null}.
67+
*/
68+
default FluentQuery<T> project(String... properties) {
69+
return project(Arrays.asList(properties));
70+
}
71+
72+
/**
73+
* Define which properties or property paths to include in the query.
74+
*
75+
* @param properties must not be {@code null}.
76+
* @return a new instance of {@link FluentQuery}.
77+
* @throws IllegalArgumentException if fields is {@code null}.
78+
*/
79+
FluentQuery<T> project(Collection<String> properties);
80+
81+
/**
82+
* Fetchable extension {@link FluentQuery} allowing to materialize results from the underlying query.
83+
*
84+
* @author Mark Paluch
85+
* @since 2.6
86+
*/
87+
interface FetchableFluentQuery<T> extends FluentQuery<T> {
88+
89+
@Override
90+
FetchableFluentQuery<T> sortBy(Sort sort);
91+
92+
@Override
93+
<R> FetchableFluentQuery<R> as(Class<R> resultType);
94+
95+
@Override
96+
default FetchableFluentQuery<T> project(String... properties) {
97+
return project(Arrays.asList(properties));
98+
}
99+
100+
@Override
101+
FetchableFluentQuery<T> project(Collection<String> properties);
102+
103+
/**
104+
* Get exactly zero or one result.
105+
*
106+
* @return {@link Optional#empty()} if no match found.
107+
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
108+
*/
109+
default Optional<T> one() {
110+
return Optional.ofNullable(oneValue());
111+
}
112+
113+
/**
114+
* Get exactly zero or one result.
115+
*
116+
* @return {@literal null} if no match found.
117+
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
118+
*/
119+
@Nullable
120+
T oneValue();
121+
122+
/**
123+
* Get the first or no result.
124+
*
125+
* @return {@link Optional#empty()} if no match found.
126+
*/
127+
default Optional<T> first() {
128+
return Optional.ofNullable(firstValue());
129+
}
130+
131+
/**
132+
* Get the first or no result.
133+
*
134+
* @return {@literal null} if no match found.
135+
*/
136+
@Nullable
137+
T firstValue();
138+
139+
/**
140+
* Get all matching elements.
141+
*
142+
* @return
143+
*/
144+
List<T> all();
145+
146+
/**
147+
* Get a page of matching elements for {@link Pageable}.
148+
*
149+
* @param pageable must not be {@code null}. The given {@link Pageable} will override any previously specified
150+
* {@link Sort sort} if the {@link Sort} object is not {@link Sort#isUnsorted()}.
151+
* @return
152+
*/
153+
Page<T> page(Pageable pageable);
154+
155+
/**
156+
* Stream all matching elements.
157+
*
158+
* @return a {@link Stream} wrapping cursors that need to be closed.
159+
*/
160+
Stream<T> stream();
161+
162+
/**
163+
* Get the number of matching elements.
164+
*
165+
* @return total number of matching elements.
166+
*/
167+
long count();
168+
169+
/**
170+
* Check for the presence of matching elements.
171+
*
172+
* @return {@literal true} if at least one matching element exists.
173+
*/
174+
boolean exists();
175+
}
176+
177+
/**
178+
* Reactive extension {@link FluentQuery} allowing to materialize results from the underlying query.
179+
*
180+
* @author Mark Paluch
181+
* @since 2.6
182+
*/
183+
interface ReactiveFluentQuery<T> extends FluentQuery<T> {
184+
185+
@Override
186+
ReactiveFluentQuery<T> sortBy(Sort sort);
187+
188+
@Override
189+
<R> ReactiveFluentQuery<R> as(Class<R> resultType);
190+
191+
@Override
192+
default ReactiveFluentQuery<T> project(String... properties) {
193+
return project(Arrays.asList(properties));
194+
}
195+
196+
@Override
197+
ReactiveFluentQuery<T> project(Collection<String> properties);
198+
199+
/**
200+
* Get exactly zero or one result.
201+
*
202+
* @return {@code null} if no match found.
203+
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
204+
*/
205+
Mono<T> one();
206+
207+
/**
208+
* Get the first or no result.
209+
*
210+
* @return {@code null} if no match found.
211+
*/
212+
Mono<T> first();
213+
214+
/**
215+
* Get all matching elements.
216+
*
217+
* @return
218+
*/
219+
Flux<T> all();
220+
221+
/**
222+
* Get a page of matching elements for {@link Pageable}.
223+
*
224+
* @param pageable must not be {@code null}. The given {@link Pageable} will override any previously specified
225+
* {@link Sort sort} if the {@link Sort} object is not {@link Sort#isUnsorted()}.
226+
* @return
227+
*/
228+
Mono<Page<T>> page(Pageable pageable);
229+
230+
/**
231+
* Get the number of matching elements.
232+
*
233+
* @return total number of matching elements.
234+
*/
235+
Mono<Long> count();
236+
237+
/**
238+
* Check for the presence of matching elements.
239+
*
240+
* @return {@literal true} if at least one matching element exists.
241+
*/
242+
Mono<Boolean> exists();
243+
244+
}
245+
}

src/main/java/org/springframework/data/repository/query/QueryByExampleExecutor.java

+12
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.repository.query;
1717

1818
import java.util.Optional;
19+
import java.util.function.Function;
1920

2021
import org.springframework.data.domain.Example;
2122
import org.springframework.data.domain.Page;
@@ -86,4 +87,15 @@ public interface QueryByExampleExecutor<T> {
8687
* @return {@literal true} if the data store contains elements that match the given {@link Example}.
8788
*/
8889
<S extends T> boolean exists(Example<S> example);
90+
91+
/**
92+
* Returns entities matching the given {@link Example} applying the {@link Function queryFunction} that defines the
93+
* query and its result type.
94+
*
95+
* @param example must not be {@literal null}.
96+
* @param queryFunction the query function defining projection, sorting, and the result type
97+
* @return all entities matching the given {@link Example}.
98+
* @since 2.6
99+
*/
100+
<S extends T, R> R findBy(Example<S> example, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction);
89101
}

src/main/java/org/springframework/data/repository/query/ReactiveQueryByExampleExecutor.java

+16
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
import reactor.core.publisher.Flux;
1919
import reactor.core.publisher.Mono;
2020

21+
import java.util.function.Function;
22+
23+
import org.reactivestreams.Publisher;
24+
2125
import org.springframework.data.domain.Example;
2226
import org.springframework.data.domain.Sort;
2327

@@ -75,4 +79,16 @@ public interface ReactiveQueryByExampleExecutor<T> {
7579
* @return {@literal true} if the data store contains elements that match the given {@link Example}.
7680
*/
7781
<S extends T> Mono<Boolean> exists(Example<S> example);
82+
83+
/**
84+
* Returns entities matching the given {@link Example} applying the {@link Function queryFunction} that defines the
85+
* query and its result type.
86+
*
87+
* @param example must not be {@literal null}.
88+
* @param queryFunction the query function defining projection, sorting, and the result type
89+
* @return all entities matching the given {@link Example}.
90+
* @since 2.6
91+
*/
92+
<S extends T, R, P extends Publisher<R>> P findBy(Example<S> example,
93+
Function<FluentQuery.ReactiveFluentQuery<S>, P> queryFunction);
7894
}

0 commit comments

Comments
 (0)