Skip to content

Commit 49cd442

Browse files
committed
Add support for fluent Querydsl and Query by Example query definition.
We now support the functional fluent query definition API for imperative and reactive usage with Querydsl and Query by Example. Page<PersonProjection> first = repository.findBy(Example.of(probe), it -> it.as(PersonProjection.class).project("firstname").page(PageRequest.of(0, 1, Sort.by("firstname")))); Closes #3757 Original pull request: #3788.
1 parent 767d97a commit 49cd442

15 files changed

+1380
-41
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

-1
Original file line numberDiff line numberDiff line change
@@ -1385,7 +1385,6 @@ public <T> T save(T objectToSave, String collectionName) {
13851385
return source.isVersionedEntity() //
13861386
? doSaveVersioned(source, collectionName) //
13871387
: (T) doSave(collectionName, objectToSave, this.mongoConverter);
1388-
13891388
}
13901389

13911390
@SuppressWarnings("unchecked")

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,9 @@ public boolean isSorted() {
164164
* @throws IllegalArgumentException when {@code fieldsObject} is {@literal null}.
165165
* @since 1.6
166166
*/
167-
protected void setFieldsObject(Document fieldsObject) {
167+
public void setFieldsObject(Document fieldsObject) {
168168

169-
Assert.notNull(sortObject, "Field document must not be null");
169+
Assert.notNull(fieldsObject, "Field document must not be null");
170170

171171
this.fieldsObject = fieldsObject;
172172
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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.mongodb.repository.support;
17+
18+
import java.util.ArrayList;
19+
import java.util.Collection;
20+
import java.util.List;
21+
22+
import org.springframework.data.domain.Sort;
23+
import org.springframework.data.repository.query.FluentQuery;
24+
import org.springframework.util.Assert;
25+
26+
/**
27+
* Support class for {@link org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery} implementations.
28+
*
29+
* @author Mark Paluch
30+
* @since 3.3
31+
*/
32+
abstract class FetchableFluentQuerySupport<P, T> implements FluentQuery.FetchableFluentQuery<T> {
33+
34+
private final P predicate;
35+
private final Sort sort;
36+
private final Class<T> resultType;
37+
private final List<String> fieldsToInclude;
38+
39+
FetchableFluentQuerySupport(P predicate, Sort sort, Class<T> resultType, List<String> fieldsToInclude) {
40+
this.predicate = predicate;
41+
this.sort = sort;
42+
this.resultType = resultType;
43+
this.fieldsToInclude = fieldsToInclude;
44+
}
45+
46+
/*
47+
* (non-Javadoc)
48+
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#sortBy(org.springframework.data.domain.Sort)
49+
*/
50+
@Override
51+
public FluentQuery.FetchableFluentQuery<T> sortBy(Sort sort) {
52+
53+
Assert.notNull(sort, "Sort must not be null!");
54+
55+
return create(predicate, sort, resultType, fieldsToInclude);
56+
}
57+
58+
/*
59+
* (non-Javadoc)
60+
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class)
61+
*/
62+
@Override
63+
public <R> FluentQuery.FetchableFluentQuery<R> as(Class<R> projection) {
64+
65+
Assert.notNull(projection, "Projection target type must not be null!");
66+
67+
return create(predicate, sort, projection, fieldsToInclude);
68+
}
69+
70+
/*
71+
* (non-Javadoc)
72+
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection)
73+
*/
74+
@Override
75+
public FluentQuery.FetchableFluentQuery<T> project(Collection<String> properties) {
76+
77+
Assert.notNull(properties, "Projection properties must not be null!");
78+
79+
return create(predicate, sort, resultType, new ArrayList<>(properties));
80+
}
81+
82+
protected abstract <R> FetchableFluentQuerySupport<P, R> create(P predicate, Sort sort, Class<R> resultType,
83+
List<String> fieldsToInclude);
84+
85+
P getPredicate() {
86+
return predicate;
87+
}
88+
89+
Sort getSort() {
90+
return sort;
91+
}
92+
93+
Class<T> getResultType() {
94+
return resultType;
95+
}
96+
97+
List<String> getFieldsToInclude() {
98+
return fieldsToInclude;
99+
}
100+
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java

+130
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,24 @@
1515
*/
1616
package org.springframework.data.mongodb.repository.support;
1717

18+
import java.util.Collections;
1819
import java.util.List;
1920
import java.util.Optional;
21+
import java.util.function.Function;
22+
import java.util.stream.Stream;
2023

24+
import org.bson.Document;
2125
import org.springframework.dao.IncorrectResultSizeDataAccessException;
2226
import org.springframework.data.domain.Page;
2327
import org.springframework.data.domain.Pageable;
2428
import org.springframework.data.domain.Sort;
2529
import org.springframework.data.mongodb.core.MongoOperations;
30+
import org.springframework.data.mongodb.core.query.BasicQuery;
2631
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
2732
import org.springframework.data.querydsl.EntityPathResolver;
2833
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
2934
import org.springframework.data.querydsl.SimpleEntityPathResolver;
35+
import org.springframework.data.repository.query.FluentQuery;
3036
import org.springframework.data.support.PageableExecutionUtils;
3137
import org.springframework.util.Assert;
3238

@@ -184,6 +190,21 @@ public boolean exists(Predicate predicate) {
184190
return createQueryFor(predicate).fetchCount() > 0;
185191
}
186192

193+
/*
194+
* (non-Javadoc)
195+
* @see org.springframework.data.querydsl.QuerydslPredicateExecutor#findBy(com.querydsl.core.types.Predicate, java.util.function.Function)
196+
*/
197+
@Override
198+
@SuppressWarnings("unchecked")
199+
public <S extends T, R> R findBy(Predicate predicate,
200+
Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {
201+
202+
Assert.notNull(predicate, "Predicate must not be null!");
203+
Assert.notNull(queryFunction, "Query function must not be null!");
204+
205+
return queryFunction.apply(new FluentQuerydsl<>(predicate, (Class<S>) typeInformation().getJavaType()));
206+
}
207+
187208
/**
188209
* Creates a {@link SpringDataMongodbQuery} for the given {@link Predicate}.
189210
*
@@ -232,4 +253,113 @@ private SpringDataMongodbQuery<T> applySorting(SpringDataMongodbQuery<T> query,
232253
toOrderSpecifiers(sort).forEach(query::orderBy);
233254
return query;
234255
}
256+
257+
/**
258+
* {@link org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery} using Querydsl
259+
* {@link Predicate}.
260+
*
261+
* @author Mark Paluch
262+
* @since 3.3
263+
*/
264+
class FluentQuerydsl<T> extends FetchableFluentQuerySupport<Predicate, T> {
265+
266+
FluentQuerydsl(Predicate predicate, Class<T> resultType) {
267+
this(predicate, Sort.unsorted(), resultType, Collections.emptyList());
268+
}
269+
270+
FluentQuerydsl(Predicate predicate, Sort sort, Class<T> resultType, List<String> fieldsToInclude) {
271+
super(predicate, sort, resultType, fieldsToInclude);
272+
}
273+
274+
@Override
275+
protected <R> FluentQuerydsl<R> create(Predicate predicate, Sort sort, Class<R> resultType,
276+
List<String> fieldsToInclude) {
277+
return new FluentQuerydsl<>(predicate, sort, resultType, fieldsToInclude);
278+
}
279+
280+
/*
281+
* (non-Javadoc)
282+
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue()
283+
*/
284+
@Override
285+
public T oneValue() {
286+
return createQuery().fetchOne();
287+
}
288+
289+
/*
290+
* (non-Javadoc)
291+
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue()
292+
*/
293+
@Override
294+
public T firstValue() {
295+
return createQuery().fetchFirst();
296+
}
297+
298+
/*
299+
* (non-Javadoc)
300+
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all()
301+
*/
302+
@Override
303+
public List<T> all() {
304+
return createQuery().fetch();
305+
}
306+
307+
/*
308+
* (non-Javadoc)
309+
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable)
310+
*/
311+
@Override
312+
public Page<T> page(Pageable pageable) {
313+
314+
Assert.notNull(pageable, "Pageable must not be null!");
315+
316+
return createQuery().fetchPage(pageable);
317+
}
318+
319+
/*
320+
* (non-Javadoc)
321+
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream()
322+
*/
323+
@Override
324+
public Stream<T> stream() {
325+
return createQuery().stream();
326+
}
327+
328+
/*
329+
* (non-Javadoc)
330+
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count()
331+
*/
332+
@Override
333+
public long count() {
334+
return createQuery().fetchCount();
335+
}
336+
337+
/*
338+
* (non-Javadoc)
339+
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists()
340+
*/
341+
@Override
342+
public boolean exists() {
343+
return count() > 0;
344+
}
345+
346+
private SpringDataMongodbQuery<T> createQuery() {
347+
return new SpringDataMongodbQuery<>(mongoOperations, typeInformation().getJavaType(), getResultType(),
348+
mongoOperations.getCollectionName(typeInformation().getJavaType()), this::customize).where(getPredicate());
349+
}
350+
351+
private void customize(BasicQuery query) {
352+
353+
List<String> fieldsToInclude = getFieldsToInclude();
354+
if (!fieldsToInclude.isEmpty()) {
355+
Document fields = new Document();
356+
fieldsToInclude.forEach(field -> fields.put(field, 1));
357+
query.setFieldsObject(fields);
358+
}
359+
360+
if (getSort().isSorted()) {
361+
query.with(getSort());
362+
}
363+
}
364+
}
235365
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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.mongodb.repository.support;
17+
18+
import java.util.ArrayList;
19+
import java.util.Collection;
20+
import java.util.List;
21+
22+
import org.springframework.data.domain.Sort;
23+
import org.springframework.data.repository.query.FluentQuery;
24+
import org.springframework.util.Assert;
25+
26+
/**
27+
* Support class for {@link org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery} implementations.
28+
*
29+
* @author Mark Paluch
30+
* @since 3.3
31+
*/
32+
abstract class ReactiveFluentQuerySupport<P, T> implements FluentQuery.ReactiveFluentQuery<T> {
33+
34+
private final P predicate;
35+
private final Sort sort;
36+
private final Class<T> resultType;
37+
private final List<String> fieldsToInclude;
38+
39+
ReactiveFluentQuerySupport(P predicate, Sort sort, Class<T> resultType, List<String> fieldsToInclude) {
40+
this.predicate = predicate;
41+
this.sort = sort;
42+
this.resultType = resultType;
43+
this.fieldsToInclude = fieldsToInclude;
44+
}
45+
46+
/*
47+
* (non-Javadoc)
48+
* @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#sortBy(org.springframework.data.domain.Sort)
49+
*/
50+
@Override
51+
public ReactiveFluentQuery<T> sortBy(Sort sort) {
52+
53+
Assert.notNull(sort, "Sort must not be null!");
54+
55+
return create(predicate, sort, resultType, fieldsToInclude);
56+
}
57+
58+
/*
59+
* (non-Javadoc)
60+
* @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#as(java.lang.Class)
61+
*/
62+
@Override
63+
public <R> ReactiveFluentQuery<R> as(Class<R> projection) {
64+
65+
Assert.notNull(projection, "Projection target type must not be null!");
66+
67+
return create(predicate, sort, projection, fieldsToInclude);
68+
}
69+
70+
/*
71+
* (non-Javadoc)
72+
* @see org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery#project(java.util.Collection)
73+
*/
74+
@Override
75+
public ReactiveFluentQuery<T> project(Collection<String> properties) {
76+
77+
Assert.notNull(properties, "Projection properties must not be null!");
78+
79+
return create(predicate, sort, resultType, new ArrayList<>(properties));
80+
}
81+
82+
protected abstract <R> ReactiveFluentQuerySupport<P, R> create(P predicate, Sort sort, Class<R> resultType,
83+
List<String> fieldsToInclude);
84+
85+
P getPredicate() {
86+
return predicate;
87+
}
88+
89+
Sort getSort() {
90+
return sort;
91+
}
92+
93+
Class<T> getResultType() {
94+
return resultType;
95+
}
96+
97+
List<String> getFieldsToInclude() {
98+
return fieldsToInclude;
99+
}
100+
}

0 commit comments

Comments
 (0)