Document that fluent findBy(…) queries must return a result.
Closes #3294
This commit is contained in:
@@ -23,6 +23,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
@@ -122,11 +123,16 @@ public interface JpaSpecificationExecutor<T> {
|
||||
/**
|
||||
* Returns entities matching the given {@link Specification} applying the {@code queryFunction} that defines the query
|
||||
* and its result type.
|
||||
* <p>
|
||||
* The query object used with {@code queryFunction} is only valid inside the {@code findBy(…)} method call. This
|
||||
* requires the query function to return a query result and not the {@link FluentQuery} object itself to ensure the
|
||||
* query is executed inside the {@code findBy(…)} method.
|
||||
*
|
||||
* @param spec must not be null.
|
||||
* @param queryFunction the query function defining projection, sorting, and the result type
|
||||
* @return all entities matching the given Example.
|
||||
* @return all entities matching the given specification.
|
||||
* @since 3.0
|
||||
* @throws InvalidDataAccessApiUsageException if the query function returns the {@link FluentQuery} instance.
|
||||
*/
|
||||
<S extends T, R> R findBy(Specification<T> spec, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.KeysetScrollPosition;
|
||||
import org.springframework.data.domain.OffsetScrollPosition;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -41,6 +42,7 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.querydsl.EntityPathResolver;
|
||||
import org.springframework.data.querydsl.QSort;
|
||||
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
|
||||
import org.springframework.data.repository.query.FluentQuery;
|
||||
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
|
||||
import org.springframework.data.support.PageableExecutionUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -245,7 +247,14 @@ public class QuerydslJpaPredicateExecutor<T> implements QuerydslPredicateExecuto
|
||||
entityManager, //
|
||||
getProjectionFactory());
|
||||
|
||||
return queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
|
||||
R result = queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
|
||||
|
||||
if (result instanceof FluentQuery<?>) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
"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");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -42,6 +42,7 @@ import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.domain.KeysetScrollPosition;
|
||||
import org.springframework.data.domain.OffsetScrollPosition;
|
||||
@@ -62,6 +63,7 @@ import org.springframework.data.jpa.repository.support.QueryHints.NoHints;
|
||||
import org.springframework.data.jpa.support.PageableUtils;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.query.FluentQuery;
|
||||
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
|
||||
import org.springframework.data.support.PageableExecutionUtils;
|
||||
import org.springframework.data.util.ProxyUtils;
|
||||
@@ -534,7 +536,14 @@ public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T
|
||||
FetchableFluentQueryBySpecification<?, T> fluentQuery = new FetchableFluentQueryBySpecification<>(spec, domainClass,
|
||||
finder, scrollDelegate, this::count, this::exists, this.entityManager, getProjectionFactory());
|
||||
|
||||
return queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
|
||||
R result = queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
|
||||
|
||||
if (result instanceof FluentQuery<?>) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
"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");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -42,6 +42,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -2355,6 +2356,14 @@ class UserRepositoryTests {
|
||||
assertThat(users).containsExactly(thirdUser, firstUser, fourthUser);
|
||||
}
|
||||
|
||||
@Test // GH-3294
|
||||
void findByFluentFailsReturningFluentQuery() {
|
||||
|
||||
User prototype = new User();
|
||||
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class)
|
||||
.isThrownBy(() -> repository.findBy(of(prototype), Function.identity()));
|
||||
}
|
||||
|
||||
@Test // GH-2294
|
||||
void findByFluentExampleFirstValue() {
|
||||
|
||||
@@ -2452,13 +2461,13 @@ class UserRepositoryTests {
|
||||
prototype.setFirstname("v");
|
||||
|
||||
List<UserProjectionUsingSpEL> users = repository.findBy(
|
||||
of(prototype,
|
||||
matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname",
|
||||
GenericPropertyMatcher::contains)), //
|
||||
q -> q.as(UserProjectionUsingSpEL.class).all());
|
||||
of(prototype,
|
||||
matching().withIgnorePaths("age", "createdAt", "active").withMatcher("firstname",
|
||||
GenericPropertyMatcher::contains)), //
|
||||
q -> q.as(UserProjectionUsingSpEL.class).all());
|
||||
|
||||
assertThat(users).extracting(UserProjectionUsingSpEL::hello)
|
||||
.contains(new GreetingsFrom().groot(firstUser.getFirstname()));
|
||||
.contains(new GreetingsFrom().groot(firstUser.getFirstname()));
|
||||
}
|
||||
|
||||
@Test // GH-2294
|
||||
|
||||
Reference in New Issue
Block a user