Skip to content

Commit 8e1f9e5

Browse files
committed
Implement FluentQuery for Querydsl and Query by Example.
Add support for both QueryByExampleExecutor and QuerydslPredicateExecutor. This manifests in SimpleJpaRepository and QuerydslJpaPredicateExecutor, resulting in various test cases proving support by both examples and Querydsl predicates. Closes #2294.
1 parent 01c4b93 commit 8e1f9e5

10 files changed

+734
-21
lines changed

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<hibernate>5.5.3.Final</hibernate>
2626
<mysql-connector-java>8.0.23</mysql-connector-java>
2727
<postgresql>42.2.19</postgresql>
28-
<springdata.commons>2.6.0-SNAPSHOT</springdata.commons>
28+
<springdata.commons>2.6.0-2228-SNAPSHOT</springdata.commons>
2929
<vavr>0.10.3</vavr>
3030

3131
<hibernate.groupId>org.hibernate</hibernate.groupId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Copyright 2013-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.jpa.repository.query;
17+
18+
import java.util.Collection;
19+
import java.util.List;
20+
import java.util.function.Function;
21+
import java.util.stream.Stream;
22+
23+
import javax.persistence.TypedQuery;
24+
25+
import org.springframework.data.domain.Example;
26+
import org.springframework.data.domain.Page;
27+
import org.springframework.data.domain.PageImpl;
28+
import org.springframework.data.domain.Pageable;
29+
import org.springframework.data.domain.Sort;
30+
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
31+
import org.springframework.data.support.PageableExecutionUtils;
32+
import org.springframework.lang.Nullable;
33+
34+
/**
35+
* Immutable implementation of {@link FetchableFluentQuery} based on Query by {@link Example}. All methods that return a
36+
* {@link FetchableFluentQuery} will return a new instance, not the original.
37+
*
38+
* @param <S> Domain type
39+
* @param <R> Result type
40+
* @author Greg Turnquist
41+
* @author Michael J. Simons
42+
* @since 2.6
43+
*/
44+
public class FetchableFluentQueryByExample<S, R> extends FluentQuerySupport<R> implements FetchableFluentQuery<R> {
45+
46+
private Example<S> example;
47+
private Function<Sort, TypedQuery<S>> finder;
48+
private Function<Example<S>, Long> countOperation;
49+
private Function<Example<S>, Boolean> existsOperation;
50+
51+
public FetchableFluentQueryByExample(Example<S> example, Class<R> resultType, Function<Sort, TypedQuery<S>> finder,
52+
Function<Example<S>, Long> countOperation, Function<Example<S>, Boolean> existsOperation) {
53+
this(example, resultType, Sort.unsorted(), null, finder, countOperation, existsOperation);
54+
}
55+
56+
private FetchableFluentQueryByExample(Example<S> example, Class<R> resultType, Sort sort,
57+
@Nullable Collection<String> properties, Function<Sort, TypedQuery<S>> finder,
58+
Function<Example<S>, Long> countOperation, Function<Example<S>, Boolean> existsOperation) {
59+
60+
super(resultType, sort, properties);
61+
this.example = example;
62+
this.finder = finder;
63+
this.countOperation = countOperation;
64+
this.existsOperation = existsOperation;
65+
}
66+
67+
@Override
68+
public FetchableFluentQuery<R> sortBy(Sort sort) {
69+
70+
return new FetchableFluentQueryByExample<>(this.example, this.resultType, this.sort.and(sort), this.properties,
71+
this.finder, this.countOperation, this.existsOperation);
72+
}
73+
74+
@Override
75+
public <NR> FetchableFluentQuery<NR> as(Class<NR> resultType) {
76+
77+
return new FetchableFluentQueryByExample<>(this.example, resultType, this.sort, this.properties, this.finder,
78+
this.countOperation, this.existsOperation);
79+
}
80+
81+
@Override
82+
public FetchableFluentQuery<R> project(Collection<String> properties) {
83+
84+
return new FetchableFluentQueryByExample<>(this.example, this.resultType, this.sort, mergeProperties(properties),
85+
this.finder, this.countOperation, this.existsOperation);
86+
}
87+
88+
@Override
89+
public R oneValue() {
90+
return firstValue();
91+
}
92+
93+
@Override
94+
public R firstValue() {
95+
96+
List<R> all = all();
97+
return all.isEmpty() ? null : all.get(0);
98+
}
99+
100+
@Override
101+
public List<R> all() {
102+
return (List<R>) this.finder.apply(this.sort).getResultList();
103+
}
104+
105+
@Override
106+
public Page<R> page(Pageable pageable) {
107+
return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable);
108+
}
109+
110+
@Override
111+
public Stream<R> stream() {
112+
return all().stream();
113+
}
114+
115+
@Override
116+
public long count() {
117+
return this.countOperation.apply(example);
118+
}
119+
120+
@Override
121+
public boolean exists() {
122+
return this.existsOperation.apply(example);
123+
}
124+
125+
private Page<R> readPage(Pageable pageable) {
126+
127+
TypedQuery<S> query = this.finder.apply(this.sort);
128+
129+
if (pageable.isPaged()) {
130+
query.setFirstResult((int) pageable.getOffset());
131+
query.setMaxResults(pageable.getPageSize());
132+
}
133+
134+
return (Page<R>) PageableExecutionUtils.getPage(query.getResultList(), pageable,
135+
() -> this.countOperation.apply(this.example));
136+
}
137+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Copyright 2013-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.jpa.repository.query;
17+
18+
import java.util.Collection;
19+
import java.util.List;
20+
import java.util.function.BiFunction;
21+
import java.util.function.Function;
22+
import java.util.stream.Stream;
23+
24+
import org.springframework.data.domain.Page;
25+
import org.springframework.data.domain.PageImpl;
26+
import org.springframework.data.domain.Pageable;
27+
import org.springframework.data.domain.Sort;
28+
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
29+
import org.springframework.data.support.PageableExecutionUtils;
30+
import org.springframework.lang.Nullable;
31+
32+
import com.querydsl.core.types.Predicate;
33+
import com.querydsl.jpa.JPQLQuery;
34+
35+
/**
36+
* Immutable implementation of {@link FetchableFluentQuery} based on a Querydsl {@link Predicate}. All methods that
37+
* return a {@link FetchableFluentQuery} will return a new instance, not the original.
38+
*
39+
* @param <S> Domain type
40+
* @param <R> Result type
41+
* @author Greg Turnquist
42+
* @author Michael J. Simons
43+
* @since 2.6
44+
*/
45+
public class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<R> implements FetchableFluentQuery<R> {
46+
47+
private Predicate predicate;
48+
private Function<Sort, JPQLQuery<S>> finder;
49+
private BiFunction<Sort, Pageable, JPQLQuery<S>> pagedFinder;
50+
private Function<Predicate, Long> countOperation;
51+
private Function<Predicate, Boolean> existsOperation;
52+
53+
public FetchableFluentQueryByPredicate(Predicate predicate, Class<R> resultType, Function<Sort, JPQLQuery<S>> finder,
54+
BiFunction<Sort, Pageable, JPQLQuery<S>> pagedFinder, Function<Predicate, Long> countOperation,
55+
Function<Predicate, Boolean> existsOperation) {
56+
this(predicate, resultType, Sort.unsorted(), null, finder, pagedFinder, countOperation, existsOperation);
57+
}
58+
59+
private FetchableFluentQueryByPredicate(Predicate predicate, Class<R> resultType, Sort sort,
60+
@Nullable Collection<String> properties, Function<Sort, JPQLQuery<S>> finder,
61+
BiFunction<Sort, Pageable, JPQLQuery<S>> pagedFinder, Function<Predicate, Long> countOperation,
62+
Function<Predicate, Boolean> existsOperation) {
63+
64+
super(resultType, sort, properties);
65+
this.predicate = predicate;
66+
this.finder = finder;
67+
this.pagedFinder = pagedFinder;
68+
this.countOperation = countOperation;
69+
this.existsOperation = existsOperation;
70+
}
71+
72+
@Override
73+
public FetchableFluentQuery<R> sortBy(Sort sort) {
74+
75+
return new FetchableFluentQueryByPredicate<>(this.predicate, this.resultType, this.sort.and(sort), this.properties,
76+
this.finder, this.pagedFinder, this.countOperation, this.existsOperation);
77+
}
78+
79+
@Override
80+
public <NR> FetchableFluentQuery<NR> as(Class<NR> resultType) {
81+
82+
return new FetchableFluentQueryByPredicate<>(this.predicate, resultType, this.sort, this.properties, this.finder,
83+
this.pagedFinder, this.countOperation, this.existsOperation);
84+
}
85+
86+
@Override
87+
public FetchableFluentQuery<R> project(Collection<String> properties) {
88+
89+
return new FetchableFluentQueryByPredicate<>(this.predicate, this.resultType, this.sort,
90+
mergeProperties(properties), this.finder, this.pagedFinder, this.countOperation, this.existsOperation);
91+
}
92+
93+
@Override
94+
public R oneValue() {
95+
return firstValue();
96+
}
97+
98+
@Override
99+
public R firstValue() {
100+
101+
List<R> all = all();
102+
return all.isEmpty() ? null : all.get(0);
103+
}
104+
105+
@Override
106+
public List<R> all() {
107+
return (List<R>) this.finder.apply(this.sort).fetchResults().getResults();
108+
}
109+
110+
@Override
111+
public Page<R> page(Pageable pageable) {
112+
return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable);
113+
}
114+
115+
@Override
116+
public Stream<R> stream() {
117+
return all().stream();
118+
}
119+
120+
@Override
121+
public long count() {
122+
return this.countOperation.apply(this.predicate);
123+
}
124+
125+
@Override
126+
public boolean exists() {
127+
return this.existsOperation.apply(this.predicate);
128+
}
129+
130+
private Page<R> readPage(Pageable pageable) {
131+
132+
JPQLQuery<S> query = this.pagedFinder.apply(this.sort, pageable);
133+
134+
return (Page<R>) PageableExecutionUtils.getPage(query.fetchResults().getResults(), pageable,
135+
() -> this.countOperation.apply(this.predicate));
136+
}
137+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2013-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.jpa.repository.query;
17+
18+
import java.util.Collection;
19+
import java.util.Collections;
20+
import java.util.HashSet;
21+
import java.util.Set;
22+
23+
import org.springframework.data.domain.Sort;
24+
import org.springframework.lang.Nullable;
25+
26+
/**
27+
* Supporting class containing some state and convenience methods for building fluent queries.
28+
*
29+
* @param <R> The resulting type of the query.
30+
* @author Greg Turnquist
31+
* @author Michael J. Simons
32+
* @since 2.6
33+
*/
34+
abstract class FluentQuerySupport<R> {
35+
36+
protected Class<R> resultType;
37+
protected Sort sort;
38+
@Nullable protected Set<String> properties;
39+
40+
FluentQuerySupport(Class<R> resultType, Sort sort, @Nullable Collection<String> properties) {
41+
42+
this.resultType = resultType;
43+
this.sort = sort;
44+
45+
if (properties != null) {
46+
this.properties = new HashSet<>(properties);
47+
} else {
48+
this.properties = null;
49+
}
50+
}
51+
52+
final Collection<String> mergeProperties(Collection<String> additionalProperties) {
53+
54+
Set<String> newProperties = new HashSet<>();
55+
if (this.properties != null) {
56+
newProperties.addAll(this.properties);
57+
}
58+
newProperties.addAll(additionalProperties);
59+
return Collections.unmodifiableCollection(newProperties);
60+
}
61+
}

0 commit comments

Comments
 (0)