Skip to content

Commit 390a82b

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());
1 parent c63453a commit 390a82b

File tree

5 files changed

+282
-0
lines changed

5 files changed

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

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)