Skip to content

Commit 83ba970

Browse files
schaudergregturn
authored andcommitted
Translate projected properties of the fluent query API into a fetchgraph.
When a property path based projection is specified we still return the root entity. But we do provide a fetchgraph. The JPA implementation will (should) load only the specified attributes eagerly. It most likely will also load all other attributes from all selected tables. Once we have infrastructure in place for for multilevel projections the same approach can and should be used for those. Currently this is not the case. Closes #2329 Original pull request: #2345.
1 parent 43305f6 commit 83ba970

10 files changed

+513
-86
lines changed

src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByExample.java

+39-24
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.ArrayList;
1919
import java.util.Collection;
20+
import java.util.Collections;
2021
import java.util.List;
2122
import java.util.function.Function;
2223
import java.util.stream.Stream;
@@ -36,7 +37,6 @@
3637
import org.springframework.data.mapping.context.MappingContext;
3738
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
3839
import org.springframework.data.support.PageableExecutionUtils;
39-
import org.springframework.lang.Nullable;
4040
import org.springframework.util.Assert;
4141

4242
/**
@@ -47,38 +47,42 @@
4747
* @param <R> Result type
4848
* @author Greg Turnquist
4949
* @author Mark Paluch
50+
* @author Jens Schauder
5051
* @since 2.6
5152
*/
52-
class FetchableFluentQueryByExample<S, R> extends FluentQuerySupport<R> implements FetchableFluentQuery<R> {
53+
class FetchableFluentQueryByExample<S, R> extends FluentQuerySupport<S, R> implements FetchableFluentQuery<R> {
5354

5455
private final Example<S> example;
5556
private final Function<Sort, TypedQuery<S>> finder;
5657
private final Function<Example<S>, Long> countOperation;
5758
private final Function<Example<S>, Boolean> existsOperation;
5859
private final EntityManager entityManager;
5960
private final EscapeCharacter escapeCharacter;
61+
private final Projector<TypedQuery<?>> projector;
6062

6163
public FetchableFluentQueryByExample(Example<S> example, Function<Sort, TypedQuery<S>> finder,
6264
Function<Example<S>, Long> countOperation, Function<Example<S>, Boolean> existsOperation,
6365
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context,
6466
EntityManager entityManager, EscapeCharacter escapeCharacter) {
65-
this(example, (Class<R>) example.getProbeType(), Sort.unsorted(), null, finder, countOperation, existsOperation,
66-
context, entityManager, escapeCharacter);
67+
this(example, example.getProbeType(), (Class<R>) example.getProbeType(), Sort.unsorted(), Collections.emptySet(),
68+
finder, countOperation, existsOperation, context, entityManager, escapeCharacter,
69+
new TypedQueryProjector(entityManager));
6770
}
6871

69-
private FetchableFluentQueryByExample(Example<S> example, Class<R> returnType, Sort sort,
70-
@Nullable Collection<String> properties, Function<Sort, TypedQuery<S>> finder,
71-
Function<Example<S>, Long> countOperation, Function<Example<S>, Boolean> existsOperation,
72+
private FetchableFluentQueryByExample(Example<S> example, Class<S> entityType, Class<R> returnType, Sort sort,
73+
Collection<String> properties, Function<Sort, TypedQuery<S>> finder, Function<Example<S>, Long> countOperation,
74+
Function<Example<S>, Boolean> existsOperation,
7275
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context,
73-
EntityManager entityManager, EscapeCharacter escapeCharacter) {
76+
EntityManager entityManager, EscapeCharacter escapeCharacter, Projector<TypedQuery<?>> projector) {
7477

75-
super(returnType, sort, properties, context);
78+
super(returnType, sort, properties, context, entityType);
7679
this.example = example;
7780
this.finder = finder;
7881
this.countOperation = countOperation;
7982
this.existsOperation = existsOperation;
8083
this.entityManager = entityManager;
8184
this.escapeCharacter = escapeCharacter;
85+
this.projector = projector;
8286
}
8387

8488
/*
@@ -90,8 +94,9 @@ public FetchableFluentQuery<R> sortBy(Sort sort) {
9094

9195
Assert.notNull(sort, "Sort must not be null!");
9296

93-
return new FetchableFluentQueryByExample<>(this.example, this.resultType, this.sort.and(sort), this.properties,
94-
this.finder, this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter);
97+
return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort.and(sort), properties, finder,
98+
countOperation, existsOperation, context, entityManager, escapeCharacter,
99+
new TypedQueryProjector(entityManager));
95100
}
96101

97102
/*
@@ -106,8 +111,9 @@ public <NR> FetchableFluentQuery<NR> as(Class<NR> resultType) {
106111
throw new UnsupportedOperationException("Class-based DTOs are not yet supported.");
107112
}
108113

109-
return new FetchableFluentQueryByExample<>(this.example, resultType, this.sort, this.properties, this.finder,
110-
this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter);
114+
return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort, properties, finder,
115+
countOperation, existsOperation, context, entityManager, escapeCharacter,
116+
new TypedQueryProjector(entityManager));
111117
}
112118

113119
/*
@@ -117,8 +123,9 @@ public <NR> FetchableFluentQuery<NR> as(Class<NR> resultType) {
117123
@Override
118124
public FetchableFluentQuery<R> project(Collection<String> properties) {
119125

120-
return new FetchableFluentQueryByExample<>(this.example, this.resultType, this.sort, mergeProperties(properties),
121-
this.finder, this.countOperation, this.existsOperation, this.context, this.entityManager, this.escapeCharacter);
126+
return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort, mergeProperties(properties),
127+
finder, countOperation, existsOperation, context, entityManager, escapeCharacter,
128+
new TypedQueryProjector(entityManager));
122129
}
123130

124131
/*
@@ -128,7 +135,7 @@ public FetchableFluentQuery<R> project(Collection<String> properties) {
128135
@Override
129136
public R oneValue() {
130137

131-
TypedQuery<S> limitedQuery = this.finder.apply(this.sort);
138+
TypedQuery<S> limitedQuery = createSortedAndProjectedQuery();
132139
limitedQuery.setMaxResults(2); // Never need more than 2 values
133140

134141
List<S> results = limitedQuery.getResultList();
@@ -147,7 +154,7 @@ public R oneValue() {
147154
@Override
148155
public R firstValue() {
149156

150-
TypedQuery<S> limitedQuery = this.finder.apply(this.sort);
157+
TypedQuery<S> limitedQuery = createSortedAndProjectedQuery();
151158
limitedQuery.setMaxResults(1); // Never need more than 1 value
152159

153160
List<S> results = limitedQuery.getResultList();
@@ -162,7 +169,7 @@ public R firstValue() {
162169
@Override
163170
public List<R> all() {
164171

165-
List<S> resultList = this.finder.apply(this.sort).getResultList();
172+
List<S> resultList = createSortedAndProjectedQuery().getResultList();
166173

167174
return convert(resultList);
168175
}
@@ -183,7 +190,7 @@ public Page<R> page(Pageable pageable) {
183190
@Override
184191
public Stream<R> stream() {
185192

186-
return this.finder.apply(this.sort) //
193+
return createSortedAndProjectedQuery() //
187194
.getResultStream() //
188195
.map(getConversionFunction());
189196
}
@@ -194,7 +201,7 @@ public Stream<R> stream() {
194201
*/
195202
@Override
196203
public long count() {
197-
return this.countOperation.apply(example);
204+
return countOperation.apply(example);
198205
}
199206

200207
/*
@@ -203,12 +210,12 @@ public long count() {
203210
*/
204211
@Override
205212
public boolean exists() {
206-
return this.existsOperation.apply(example);
213+
return existsOperation.apply(example);
207214
}
208215

209216
private Page<R> readPage(Pageable pageable) {
210217

211-
TypedQuery<S> pagedQuery = this.finder.apply(this.sort);
218+
TypedQuery<S> pagedQuery = createSortedAndProjectedQuery();
212219

213220
if (pageable.isPaged()) {
214221
pagedQuery.setFirstResult((int) pageable.getOffset());
@@ -217,7 +224,15 @@ private Page<R> readPage(Pageable pageable) {
217224

218225
List<R> paginatedResults = convert(pagedQuery.getResultList());
219226

220-
return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> this.countOperation.apply(this.example));
227+
return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> countOperation.apply(example));
228+
}
229+
230+
private TypedQuery<S> createSortedAndProjectedQuery() {
231+
232+
TypedQuery<S> query = finder.apply(sort);
233+
projector.apply(entityType, query, properties);
234+
235+
return query;
221236
}
222237

223238
private List<R> convert(List<S> resultList) {
@@ -232,7 +247,7 @@ private List<R> convert(List<S> resultList) {
232247
}
233248

234249
private Function<Object, R> getConversionFunction() {
235-
return getConversionFunction(this.example.getProbeType(), this.resultType);
250+
return getConversionFunction(example.getProbeType(), resultType);
236251
}
237252

238253
}

0 commit comments

Comments
 (0)