Skip to content

Commit 82d845b

Browse files
committed
Apply hints based on property paths also for example based queries.
Closes #2329
1 parent 2ec90d2 commit 82d845b

11 files changed

+292
-167
lines changed

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

-76
This file was deleted.

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

+35-20
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<>(this.example, this.entityType, this.resultType, this.sort.and(sort),
98+
this.properties, this.finder, this.countOperation, this.existsOperation, this.context, this.entityManager,
99+
this.escapeCharacter, 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<>(this.example, this.entityType, resultType, this.sort, this.properties,
115+
this.finder, this.countOperation, this.existsOperation, this.context, this.entityManager, this.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<>(this.example, this.entityType, this.resultType, this.sort,
127+
mergeProperties(properties), this.finder, this.countOperation, this.existsOperation, this.context,
128+
this.entityManager, this.escapeCharacter, 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
}
@@ -208,7 +215,7 @@ public boolean exists() {
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());
@@ -220,6 +227,14 @@ private Page<R> readPage(Pageable pageable) {
220227
return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> this.countOperation.apply(this.example));
221228
}
222229

230+
private TypedQuery<S> createSortedAndProjectedQuery() {
231+
232+
TypedQuery<S> query = this.finder.apply(this.sort);
233+
projector.apply(entityType, query, properties);
234+
235+
return query;
236+
}
237+
223238
private List<R> convert(List<S> resultList) {
224239

225240
Function<Object, R> conversionFunction = getConversionFunction();

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

+30-27
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.BiFunction;
2223
import java.util.function.Function;
@@ -32,7 +33,6 @@
3233
import org.springframework.data.mapping.context.MappingContext;
3334
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
3435
import org.springframework.data.support.PageableExecutionUtils;
35-
import org.springframework.lang.Nullable;
3636
import org.springframework.util.Assert;
3737

3838
import com.querydsl.core.types.Predicate;
@@ -49,37 +49,37 @@
4949
* @author Jens Schauder
5050
* @since 2.6
5151
*/
52-
class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<R> implements FetchableFluentQuery<R> {
52+
class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> implements FetchableFluentQuery<R> {
5353

5454
private final Predicate predicate;
5555
private final Function<Sort, AbstractJPAQuery<?, ?>> finder;
5656
private final BiFunction<Sort, Pageable, AbstractJPAQuery<?, ?>> pagedFinder;
5757
private final Function<Predicate, Long> countOperation;
5858
private final Function<Predicate, Boolean> existsOperation;
59-
private final Class<S> entityType;
60-
private final Projector projector;
59+
private final Projector<AbstractJPAQuery<?, ?>> projector;
6160

62-
public FetchableFluentQueryByPredicate(Predicate predicate, Class<R> resultType,
61+
public FetchableFluentQueryByPredicate(Predicate predicate, Class<S> entityType,
6362
Function<Sort, AbstractJPAQuery<?, ?>> finder, BiFunction<Sort, Pageable, AbstractJPAQuery<?, ?>> pagedFinder,
64-
Function<Predicate, Long> countOperation, Function<Predicate, Boolean> existsOperation, Class<S> entityType,
65-
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context, Projector projector) {
66-
this(predicate, resultType, Sort.unsorted(), null, finder, pagedFinder, countOperation, existsOperation, entityType,
67-
context, projector);
63+
Function<Predicate, Long> countOperation, Function<Predicate, Boolean> existsOperation,
64+
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context,
65+
Projector<AbstractJPAQuery<?, ?>> projector) {
66+
this(predicate, entityType, (Class<R>) entityType, Sort.unsorted(), Collections.emptySet(), finder, pagedFinder,
67+
countOperation, existsOperation, context, projector);
6868
}
6969

70-
private FetchableFluentQueryByPredicate(Predicate predicate, Class<R> resultType, Sort sort,
71-
@Nullable Collection<String> properties, Function<Sort, AbstractJPAQuery<?, ?>> finder,
70+
private FetchableFluentQueryByPredicate(Predicate predicate, Class<S> entityType, Class<R> resultType, Sort sort,
71+
Collection<String> properties, Function<Sort, AbstractJPAQuery<?, ?>> finder,
7272
BiFunction<Sort, Pageable, AbstractJPAQuery<?, ?>> pagedFinder, Function<Predicate, Long> countOperation,
73-
Function<Predicate, Boolean> existsOperation, Class<S> entityType,
74-
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context, Projector projector) {
73+
Function<Predicate, Boolean> existsOperation,
74+
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context,
75+
Projector<AbstractJPAQuery<?, ?>> projector) {
7576

76-
super(resultType, sort, properties, context);
77+
super(resultType, sort, properties, context, entityType);
7778
this.predicate = predicate;
7879
this.finder = finder;
7980
this.pagedFinder = pagedFinder;
8081
this.countOperation = countOperation;
8182
this.existsOperation = existsOperation;
82-
this.entityType = entityType;
8383
this.projector = projector;
8484
}
8585

@@ -92,8 +92,8 @@ public FetchableFluentQuery<R> sortBy(Sort sort) {
9292

9393
Assert.notNull(sort, "Sort must not be null!");
9494

95-
return new FetchableFluentQueryByPredicate<>(this.predicate, this.resultType, this.sort.and(sort), this.properties,
96-
this.finder, this.pagedFinder, this.countOperation, this.existsOperation, this.entityType, this.context,
95+
return new FetchableFluentQueryByPredicate<>(this.predicate, this.entityType, this.resultType, this.sort.and(sort),
96+
this.properties, this.finder, this.pagedFinder, this.countOperation, this.existsOperation, this.context,
9797
this.projector);
9898
}
9999

@@ -105,12 +105,14 @@ public FetchableFluentQuery<R> sortBy(Sort sort) {
105105
public <NR> FetchableFluentQuery<NR> as(Class<NR> resultType) {
106106

107107
Assert.notNull(resultType, "Projection target type must not be null!");
108+
108109
if (!resultType.isInterface()) {
109110
throw new UnsupportedOperationException("Class-based DTOs are not yet supported.");
110111
}
111112

112-
return new FetchableFluentQueryByPredicate<>(this.predicate, resultType, this.sort, this.properties, this.finder,
113-
this.pagedFinder, this.countOperation, this.existsOperation, this.entityType, this.context, this.projector);
113+
return new FetchableFluentQueryByPredicate<>(this.predicate, this.entityType, resultType, this.sort,
114+
this.properties, this.finder, this.pagedFinder, this.countOperation, this.existsOperation, this.context,
115+
this.projector);
114116
}
115117

116118
/*
@@ -120,9 +122,9 @@ public <NR> FetchableFluentQuery<NR> as(Class<NR> resultType) {
120122
@Override
121123
public FetchableFluentQuery<R> project(Collection<String> properties) {
122124

123-
return new FetchableFluentQueryByPredicate<>(this.predicate, this.resultType, this.sort,
125+
return new FetchableFluentQueryByPredicate<>(this.predicate, this.entityType, this.resultType, this.sort,
124126
mergeProperties(properties), this.finder, this.pagedFinder, this.countOperation, this.existsOperation,
125-
this.entityType, this.context, this.projector);
127+
this.context, this.projector);
126128
}
127129

128130
/*
@@ -187,12 +189,6 @@ public Stream<R> stream() {
187189
.map(getConversionFunction());
188190
}
189191

190-
private AbstractJPAQuery<?, ?> createSortedAndProjectedQuery() {
191-
192-
final AbstractJPAQuery<?, ?> query = this.finder.apply(this.sort);
193-
return projector.apply(entityType, query, properties);
194-
}
195-
196192
/*
197193
* (non-Javadoc)
198194
* @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count()
@@ -211,6 +207,13 @@ public boolean exists() {
211207
return this.existsOperation.apply(this.predicate);
212208
}
213209

210+
private AbstractJPAQuery<?, ?> createSortedAndProjectedQuery() {
211+
212+
final AbstractJPAQuery<?, ?> query = this.finder.apply(this.sort);
213+
projector.apply(entityType, query, properties);
214+
return query;
215+
}
216+
214217
private Page<R> readPage(Pageable pageable) {
215218

216219
AbstractJPAQuery<?, ?> pagedQuery = this.pagedFinder.apply(this.sort, pageable);

0 commit comments

Comments
 (0)