Skip to content

Commit 729b00d

Browse files
committed
Document that fluent findBy(…) queries must return a result.
Closes #3294
1 parent 74af5c1 commit 729b00d

File tree

4 files changed

+41
-5
lines changed

4 files changed

+41
-5
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Optional;
2020
import java.util.function.Function;
2121

22+
import org.springframework.dao.InvalidDataAccessApiUsageException;
2223
import org.springframework.data.domain.Page;
2324
import org.springframework.data.domain.Pageable;
2425
import org.springframework.data.domain.Sort;
@@ -190,6 +191,10 @@ default long delete(PredicateSpecification<T> spec) {
190191
/**
191192
* Returns entities matching the given {@link Specification} applying the {@code queryFunction} that defines the query
192193
* and its result type.
194+
* <p>
195+
* The query object used with {@code queryFunction} is only valid inside the {@code findBy(…)} method call. This
196+
* requires the query function to return a query result and not the {@link FluentQuery} object itself to ensure the
197+
* query is executed inside the {@code findBy(…)} method.
193198
*
194199
* @param spec must not be null.
195200
* @param queryFunction the query function defining projection, sorting, and the result type
@@ -204,11 +209,16 @@ default <S extends T, R> R findBy(PredicateSpecification<T> spec,
204209
/**
205210
* Returns entities matching the given {@link Specification} applying the {@code queryFunction} that defines the query
206211
* and its result type.
212+
* <p>
213+
* The query object used with {@code queryFunction} is only valid inside the {@code findBy(…)} method call. This
214+
* requires the query function to return a query result and not the {@link FluentQuery} object itself to ensure the
215+
* query is executed inside the {@code findBy(…)} method.
207216
*
208217
* @param spec must not be null.
209218
* @param queryFunction the query function defining projection, sorting, and the result type
210-
* @return all entities matching the given Example.
219+
* @return all entities matching the given specification.
211220
* @since 3.0
221+
* @throws InvalidDataAccessApiUsageException if the query function returns the {@link FluentQuery} instance.
212222
*/
213223
<S extends T, R> R findBy(Specification<T> spec, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction);
214224

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.function.Function;
2525

2626
import org.springframework.dao.IncorrectResultSizeDataAccessException;
27+
import org.springframework.dao.InvalidDataAccessApiUsageException;
2728
import org.springframework.data.domain.KeysetScrollPosition;
2829
import org.springframework.data.domain.OffsetScrollPosition;
2930
import org.springframework.data.domain.Page;
@@ -40,6 +41,7 @@
4041
import org.springframework.data.querydsl.EntityPathResolver;
4142
import org.springframework.data.querydsl.QSort;
4243
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
44+
import org.springframework.data.repository.query.FluentQuery;
4345
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
4446
import org.springframework.data.support.PageableExecutionUtils;
4547
import org.springframework.lang.Nullable;
@@ -235,8 +237,7 @@ public <S extends T, R> R findBy(Predicate predicate, Function<FetchableFluentQu
235237
};
236238

237239
FetchableFluentQueryByPredicate<T, T> fluentQuery = new FetchableFluentQueryByPredicate<>( //
238-
path,
239-
predicate, //
240+
path, predicate, //
240241
this.entityInformation, //
241242
finder, //
242243
scroll, //
@@ -246,7 +247,14 @@ public <S extends T, R> R findBy(Predicate predicate, Function<FetchableFluentQu
246247
entityManager, //
247248
getProjectionFactory());
248249

249-
return queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
250+
R result = queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
251+
252+
if (result instanceof FluentQuery<?>) {
253+
throw new InvalidDataAccessApiUsageException(
254+
"findBy(…) queries must result a query result and not the FluentQuery object to ensure that queries are executed within the scope of the findBy(…) method");
255+
}
256+
257+
return result;
250258
}
251259

252260
@Override

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import java.util.function.BiConsumer;
4141
import java.util.function.Function;
4242

43+
import org.springframework.dao.InvalidDataAccessApiUsageException;
4344
import org.springframework.data.domain.Example;
4445
import org.springframework.data.domain.KeysetScrollPosition;
4546
import org.springframework.data.domain.OffsetScrollPosition;
@@ -65,6 +66,7 @@
6566
import org.springframework.data.mapping.PropertyPath;
6667
import org.springframework.data.projection.ProjectionFactory;
6768
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
69+
import org.springframework.data.repository.query.FluentQuery;
6870
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
6971
import org.springframework.data.repository.query.ReturnedType;
7072
import org.springframework.data.support.PageableExecutionUtils;
@@ -542,7 +544,14 @@ private <S extends T, R> R doFindBy(Specification<T> spec, Class<T> domainClass,
542544
FetchableFluentQueryBySpecification<?, T> fluentQuery = new FetchableFluentQueryBySpecification<>(spec, domainClass,
543545
finder, scrollDelegate, this::count, this::exists, this.entityManager, getProjectionFactory());
544546

545-
return queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
547+
R result = queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
548+
549+
if (result instanceof FluentQuery<?>) {
550+
throw new InvalidDataAccessApiUsageException(
551+
"findBy(…) queries must result a query result and not the FluentQuery object to ensure that queries are executed within the scope of the findBy(…) method");
552+
}
553+
554+
return result;
546555
}
547556

548557
@Override

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java

+9
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import java.util.Map;
4141
import java.util.Optional;
4242
import java.util.Set;
43+
import java.util.function.Function;
4344
import java.util.stream.IntStream;
4445
import java.util.stream.Stream;
4546

@@ -2427,6 +2428,14 @@ void findByFluentExampleWithSorting() {
24272428
assertThat(users).containsExactly(thirdUser, firstUser, fourthUser);
24282429
}
24292430

2431+
@Test // GH-3294
2432+
void findByFluentFailsReturningFluentQuery() {
2433+
2434+
User prototype = new User();
2435+
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class)
2436+
.isThrownBy(() -> repository.findBy(of(prototype), Function.identity()));
2437+
}
2438+
24302439
@Test // GH-2294
24312440
void findByFluentExampleFirstValue() {
24322441

0 commit comments

Comments
 (0)