Fluent query API should use ProjectionFactory provided by the RepositoryFactory.
This commit makes sure to push the ProjectionFactory down to the fluent query to make use of beans registered in the context. Prior to this change the fluent query variant would host its own factory not being aware of its surroundings. Original pull request: #3432 Closes: #3410
This commit is contained in:
committed by
Mark Paluch
parent
4d720316c3
commit
0af9c62e04
@@ -21,6 +21,7 @@ import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -85,4 +86,11 @@ public interface CrudMethodMetadata {
|
||||
* @since 1.9
|
||||
*/
|
||||
Method getMethod();
|
||||
|
||||
/**
|
||||
* @return the {@link ProjectionFactory} to use or {@literal null} if not present.
|
||||
* @since ??
|
||||
*/
|
||||
@Nullable
|
||||
ProjectionFactory getProjectionFactory();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
@@ -38,6 +39,7 @@ import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.jpa.repository.Lock;
|
||||
import org.springframework.data.jpa.repository.Meta;
|
||||
import org.springframework.data.jpa.repository.QueryHints;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -61,6 +63,11 @@ import org.springframework.util.ReflectionUtils;
|
||||
class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, BeanClassLoaderAware {
|
||||
|
||||
private @Nullable ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
|
||||
private final Supplier<ProjectionFactory> projectionFactorySupplier;
|
||||
|
||||
CrudMethodMetadataPostProcessor(Supplier<ProjectionFactory> projectionFactorySupplier) {
|
||||
this.projectionFactorySupplier = projectionFactorySupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
@@ -69,7 +76,8 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B
|
||||
|
||||
@Override
|
||||
public void postProcess(ProxyFactory factory, RepositoryInformation repositoryInformation) {
|
||||
factory.addAdvice(new CrudMethodMetadataPopulatingMethodInterceptor(repositoryInformation));
|
||||
factory
|
||||
.addAdvice(new CrudMethodMetadataPopulatingMethodInterceptor(repositoryInformation, projectionFactorySupplier));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,11 +109,14 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B
|
||||
|
||||
private final ConcurrentMap<Method, CrudMethodMetadata> metadataCache = new ConcurrentHashMap<>();
|
||||
private final Set<Method> implementations = new HashSet<>();
|
||||
private final Supplier<ProjectionFactory> projectionFactory;
|
||||
|
||||
CrudMethodMetadataPopulatingMethodInterceptor(RepositoryInformation repositoryInformation) {
|
||||
CrudMethodMetadataPopulatingMethodInterceptor(RepositoryInformation repositoryInformation,
|
||||
Supplier<ProjectionFactory> projectionFactory) {
|
||||
|
||||
ReflectionUtils.doWithMethods(repositoryInformation.getRepositoryInterface(), implementations::add,
|
||||
method -> !repositoryInformation.isQueryMethod(method));
|
||||
this.projectionFactory = projectionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +161,7 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B
|
||||
|
||||
if (methodMetadata == null) {
|
||||
|
||||
methodMetadata = new DefaultCrudMethodMetadata(method);
|
||||
methodMetadata = new DefaultCrudMethodMetadata(method, projectionFactory.get());
|
||||
CrudMethodMetadata tmp = metadataCache.putIfAbsent(method, methodMetadata);
|
||||
|
||||
if (tmp != null) {
|
||||
@@ -185,15 +196,17 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B
|
||||
private final @Nullable String comment;
|
||||
private final Optional<EntityGraph> entityGraph;
|
||||
private final Method method;
|
||||
private ProjectionFactory projectionFactory;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultCrudMethodMetadata} for the given {@link Method}.
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
*/
|
||||
DefaultCrudMethodMetadata(Method method) {
|
||||
DefaultCrudMethodMetadata(Method method, ProjectionFactory projectionFactory) {
|
||||
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
this.projectionFactory = projectionFactory;
|
||||
|
||||
this.lockModeType = findLockModeType(method);
|
||||
this.queryHints = findQueryHints(method, it -> true);
|
||||
@@ -274,6 +287,11 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectionFactory getProjectionFactory() {
|
||||
return projectionFactory;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ThreadBoundTargetSource implements TargetSource {
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.data.domain.ScrollPosition;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Window;
|
||||
import org.springframework.data.jpa.repository.query.ScrollDelegate;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
|
||||
import org.springframework.data.support.PageableExecutionUtils;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -51,6 +52,7 @@ import com.querydsl.jpa.impl.AbstractJPAQuery;
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @author J.R. Onyschak
|
||||
* @author Christoph Strobl
|
||||
* @since 2.6
|
||||
*/
|
||||
class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> implements FetchableFluentQuery<R> {
|
||||
@@ -64,21 +66,21 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> imp
|
||||
private final Function<Predicate, Boolean> existsOperation;
|
||||
private final EntityManager entityManager;
|
||||
|
||||
public FetchableFluentQueryByPredicate(Predicate predicate, Class<S> entityType,
|
||||
FetchableFluentQueryByPredicate(Predicate predicate, Class<S> entityType,
|
||||
Function<Sort, AbstractJPAQuery<?, ?>> finder, PredicateScrollDelegate<S> scroll,
|
||||
BiFunction<Sort, Pageable, AbstractJPAQuery<?, ?>> pagedFinder, Function<Predicate, Long> countOperation,
|
||||
Function<Predicate, Boolean> existsOperation, EntityManager entityManager) {
|
||||
Function<Predicate, Boolean> existsOperation, EntityManager entityManager, ProjectionFactory projectionFactory) {
|
||||
this(predicate, entityType, (Class<R>) entityType, Sort.unsorted(), 0, Collections.emptySet(), finder, scroll,
|
||||
pagedFinder, countOperation, existsOperation, entityManager);
|
||||
pagedFinder, countOperation, existsOperation, entityManager, projectionFactory);
|
||||
}
|
||||
|
||||
private FetchableFluentQueryByPredicate(Predicate predicate, Class<S> entityType, Class<R> resultType, Sort sort,
|
||||
int limit, Collection<String> properties, Function<Sort, AbstractJPAQuery<?, ?>> finder,
|
||||
PredicateScrollDelegate<S> scroll, BiFunction<Sort, Pageable, AbstractJPAQuery<?, ?>> pagedFinder,
|
||||
Function<Predicate, Long> countOperation, Function<Predicate, Boolean> existsOperation,
|
||||
EntityManager entityManager) {
|
||||
EntityManager entityManager, ProjectionFactory projectionFactory) {
|
||||
|
||||
super(resultType, sort, limit, properties, entityType);
|
||||
super(resultType, sort, limit, properties, entityType, projectionFactory);
|
||||
this.predicate = predicate;
|
||||
this.finder = finder;
|
||||
this.scroll = scroll;
|
||||
@@ -94,7 +96,8 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> imp
|
||||
Assert.notNull(sort, "Sort must not be null");
|
||||
|
||||
return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, this.sort.and(sort), limit,
|
||||
properties, finder, scroll, pagedFinder, countOperation, existsOperation, entityManager);
|
||||
properties, finder, scroll, pagedFinder, countOperation, existsOperation, entityManager,
|
||||
getProjectionFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,7 +106,7 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> imp
|
||||
Assert.isTrue(limit >= 0, "Limit must not be negative");
|
||||
|
||||
return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort, limit, properties, finder,
|
||||
scroll, pagedFinder, countOperation, existsOperation, entityManager);
|
||||
scroll, pagedFinder, countOperation, existsOperation, entityManager, getProjectionFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,14 +119,15 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> imp
|
||||
}
|
||||
|
||||
return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort, limit, properties, finder,
|
||||
scroll, pagedFinder, countOperation, existsOperation, entityManager);
|
||||
scroll, pagedFinder, countOperation, existsOperation, entityManager, getProjectionFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchableFluentQuery<R> project(Collection<String> properties) {
|
||||
|
||||
return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort, limit,
|
||||
mergeProperties(properties), finder, scroll, pagedFinder, countOperation, existsOperation, entityManager);
|
||||
mergeProperties(properties), finder, scroll, pagedFinder, countOperation, existsOperation, entityManager,
|
||||
getProjectionFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -230,7 +234,6 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> imp
|
||||
return getConversionFunction(entityType, resultType);
|
||||
}
|
||||
|
||||
|
||||
static class PredicateScrollDelegate<T> extends ScrollDelegate<T> {
|
||||
|
||||
private final ScrollQueryFactory scrollFunction;
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.data.domain.Window;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.query.ScrollDelegate;
|
||||
import org.springframework.data.jpa.support.PageableUtils;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.query.FluentQuery;
|
||||
import org.springframework.data.support.PageableExecutionUtils;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -47,6 +48,7 @@ import org.springframework.util.Assert;
|
||||
* @param <S> Domain type
|
||||
* @param <R> Result type
|
||||
* @author Greg Turnquist
|
||||
* @author Christoph Strobl
|
||||
* @since 3.0
|
||||
*/
|
||||
class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
@@ -59,20 +61,21 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
private final Function<Specification<S>, Boolean> existsOperation;
|
||||
private final EntityManager entityManager;
|
||||
|
||||
public FetchableFluentQueryBySpecification(Specification<S> spec, Class<S> entityType,
|
||||
Function<Sort, TypedQuery<S>> finder, SpecificationScrollDelegate<S> scrollDelegate,
|
||||
Function<Specification<S>, Long> countOperation, Function<Specification<S>, Boolean> existsOperation,
|
||||
EntityManager entityManager) {
|
||||
FetchableFluentQueryBySpecification(Specification<S> spec, Class<S> entityType, Function<Sort, TypedQuery<S>> finder,
|
||||
SpecificationScrollDelegate<S> scrollDelegate, Function<Specification<S>, Long> countOperation,
|
||||
Function<Specification<S>, Boolean> existsOperation, EntityManager entityManager,
|
||||
ProjectionFactory projectionFactory) {
|
||||
this(spec, entityType, (Class<R>) entityType, Sort.unsorted(), 0, Collections.emptySet(), finder, scrollDelegate,
|
||||
countOperation, existsOperation, entityManager);
|
||||
countOperation, existsOperation, entityManager, projectionFactory);
|
||||
}
|
||||
|
||||
private FetchableFluentQueryBySpecification(Specification<S> spec, Class<S> entityType, Class<R> resultType,
|
||||
Sort sort, int limit, Collection<String> properties, Function<Sort, TypedQuery<S>> finder,
|
||||
SpecificationScrollDelegate<S> scrollDelegate, Function<Specification<S>, Long> countOperation,
|
||||
Function<Specification<S>, Boolean> existsOperation, EntityManager entityManager) {
|
||||
Function<Specification<S>, Boolean> existsOperation, EntityManager entityManager,
|
||||
ProjectionFactory projectionFactory) {
|
||||
|
||||
super(resultType, sort, limit, properties, entityType);
|
||||
super(resultType, sort, limit, properties, entityType, projectionFactory);
|
||||
this.spec = spec;
|
||||
this.finder = finder;
|
||||
this.scroll = scrollDelegate;
|
||||
@@ -87,7 +90,7 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
Assert.notNull(sort, "Sort must not be null");
|
||||
|
||||
return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, this.sort.and(sort), limit,
|
||||
properties, finder, scroll, countOperation, existsOperation, entityManager);
|
||||
properties, finder, scroll, countOperation, existsOperation, entityManager, getProjectionFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,7 +99,7 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
Assert.isTrue(limit >= 0, "Limit must not be negative");
|
||||
|
||||
return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, this.sort.and(sort), limit,
|
||||
properties, finder, scroll, countOperation, existsOperation, entityManager);
|
||||
properties, finder, scroll, countOperation, existsOperation, entityManager, getProjectionFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,14 +111,14 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
}
|
||||
|
||||
return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, sort, limit, properties, finder,
|
||||
scroll, countOperation, existsOperation, entityManager);
|
||||
scroll, countOperation, existsOperation, entityManager, getProjectionFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchableFluentQuery<R> project(Collection<String> properties) {
|
||||
|
||||
return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, sort, limit, properties, finder,
|
||||
scroll, countOperation, existsOperation, entityManager);
|
||||
scroll, countOperation, existsOperation, entityManager, getProjectionFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.function.Function;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.domain.ScrollPosition;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -36,6 +37,7 @@ import org.springframework.lang.Nullable;
|
||||
* @author Greg Turnquist
|
||||
* @author Jens Schauder
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 2.6
|
||||
*/
|
||||
abstract class FluentQuerySupport<S, R> {
|
||||
@@ -46,10 +48,10 @@ abstract class FluentQuerySupport<S, R> {
|
||||
protected final Set<String> properties;
|
||||
protected final Class<S> entityType;
|
||||
|
||||
private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory();
|
||||
private final ProjectionFactory projectionFactory;
|
||||
|
||||
FluentQuerySupport(Class<R> resultType, Sort sort, int limit, @Nullable Collection<String> properties,
|
||||
Class<S> entityType) {
|
||||
Class<S> entityType, ProjectionFactory projectionFactory) {
|
||||
|
||||
this.resultType = resultType;
|
||||
this.sort = sort;
|
||||
@@ -62,6 +64,11 @@ abstract class FluentQuerySupport<S, R> {
|
||||
}
|
||||
|
||||
this.entityType = entityType;
|
||||
this.projectionFactory = projectionFactory;
|
||||
}
|
||||
|
||||
ProjectionFactory getProjectionFactory() {
|
||||
return projectionFactory;
|
||||
}
|
||||
|
||||
final Collection<String> mergeProperties(Collection<String> additionalProperties) {
|
||||
|
||||
@@ -100,7 +100,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
this.entityManager = entityManager;
|
||||
this.extractor = PersistenceProvider.fromEntityManager(entityManager);
|
||||
this.crudMethodMetadataPostProcessor = new CrudMethodMetadataPostProcessor();
|
||||
this.crudMethodMetadataPostProcessor = new CrudMethodMetadataPostProcessor(() -> getProjectionFactory());
|
||||
this.entityPathResolver = SimpleEntityPathResolver.INSTANCE;
|
||||
this.queryMethodFactory = new DefaultJpaQueryMethodFactory(extractor);
|
||||
this.queryRewriterProvider = QueryRewriterProvider.simple();
|
||||
|
||||
@@ -36,9 +36,12 @@ import org.springframework.data.jpa.repository.query.KeysetScrollDelegate.QueryS
|
||||
import org.springframework.data.jpa.repository.query.KeysetScrollSpecification;
|
||||
import org.springframework.data.jpa.repository.support.FetchableFluentQueryByPredicate.PredicateScrollDelegate;
|
||||
import org.springframework.data.jpa.repository.support.FluentQuerySupport.ScrollQueryFactory;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
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.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
|
||||
import org.springframework.data.support.PageableExecutionUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -223,7 +226,8 @@ public class QuerydslJpaPredicateExecutor<T> implements QuerydslPredicateExecuto
|
||||
pagedFinder, //
|
||||
this::count, //
|
||||
this::exists, //
|
||||
entityManager //
|
||||
entityManager, //
|
||||
getProjectionFactory()
|
||||
);
|
||||
|
||||
return queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
|
||||
@@ -251,7 +255,6 @@ public class QuerydslJpaPredicateExecutor<T> implements QuerydslPredicateExecuto
|
||||
|
||||
AbstractJPAQuery<?, ?> query = doCreateQuery(getQueryHints().withFetchGraphs(entityManager), predicate);
|
||||
CrudMethodMetadata metadata = getRepositoryMethodMetadata();
|
||||
|
||||
if (metadata == null) {
|
||||
return query;
|
||||
}
|
||||
@@ -331,6 +334,16 @@ public class QuerydslJpaPredicateExecutor<T> implements QuerydslPredicateExecuto
|
||||
return querydsl.applySorting(sort, query).fetch();
|
||||
}
|
||||
|
||||
private ProjectionFactory getProjectionFactory() {
|
||||
|
||||
CrudMethodMetadata metadata = getRepositoryMethodMetadata();
|
||||
if(metadata == null || metadata.getProjectionFactory() == null) {
|
||||
return new SpelAwareProxyProjectionFactory();
|
||||
}
|
||||
|
||||
return metadata.getProjectionFactory();
|
||||
}
|
||||
|
||||
class QuerydslQueryStrategy implements QueryStrategy<Expression<?>, BooleanExpression> {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -61,6 +61,8 @@ import org.springframework.data.jpa.repository.support.FetchableFluentQueryBySpe
|
||||
import org.springframework.data.jpa.repository.support.FluentQuerySupport.ScrollQueryFactory;
|
||||
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.FetchableFluentQuery;
|
||||
import org.springframework.data.support.PageableExecutionUtils;
|
||||
import org.springframework.data.util.ProxyUtils;
|
||||
@@ -524,8 +526,8 @@ public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T
|
||||
|
||||
SpecificationScrollDelegate<T> scrollDelegate = new SpecificationScrollDelegate<>(scrollFunction,
|
||||
entityInformation);
|
||||
FetchableFluentQuery<T> fluentQuery = new FetchableFluentQueryBySpecification<>(spec, domainClass, finder,
|
||||
scrollDelegate, this::count, this::exists, this.entityManager);
|
||||
FetchableFluentQueryBySpecification<?, T> fluentQuery = new FetchableFluentQueryBySpecification<>(spec, domainClass, finder,
|
||||
scrollDelegate, this::count, this::exists, this.entityManager, getProjectionFactory());
|
||||
|
||||
return queryFunction.apply((FetchableFluentQuery<S>) fluentQuery);
|
||||
}
|
||||
@@ -903,6 +905,16 @@ public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T
|
||||
}
|
||||
}
|
||||
|
||||
private ProjectionFactory getProjectionFactory() {
|
||||
|
||||
CrudMethodMetadata metadata = getRepositoryMethodMetadata();
|
||||
if(metadata == null || metadata.getProjectionFactory() == null) {
|
||||
return new SpelAwareProxyProjectionFactory();
|
||||
}
|
||||
|
||||
return metadata.getProjectionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a count query and transparently sums up all values returned.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jpa.repository;
|
||||
|
||||
/**
|
||||
* A trivial component registered via {@literal appication-context.xml} to be called from SpEL.
|
||||
*/
|
||||
public class GreetingsFrom {
|
||||
|
||||
public String groot(String name) {
|
||||
return "(%s) - I am Groot!".formatted(name);
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,11 @@ class JavaConfigUserRepositoryTests extends UserRepositoryTests {
|
||||
return factory.getObject();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GreetingsFrom greetingsFrom() {
|
||||
return new GreetingsFrom();
|
||||
}
|
||||
|
||||
private NamedQueries namedQueries() throws IOException {
|
||||
|
||||
PropertiesFactoryBean factory = new PropertiesFactoryBean();
|
||||
|
||||
@@ -51,6 +51,7 @@ import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
@@ -67,6 +68,7 @@ import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.repository.sample.SampleEvaluationContextExtension.SampleSecurityContextHolder;
|
||||
import org.springframework.data.jpa.repository.sample.UserRepository;
|
||||
import org.springframework.data.jpa.repository.sample.UserRepository.NameOnly;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -2437,6 +2439,24 @@ class UserRepositoryTests {
|
||||
.containsExactlyInAnyOrder(firstUser.getFirstname(), thirdUser.getFirstname(), fourthUser.getFirstname());
|
||||
}
|
||||
|
||||
@Test // GH-3410
|
||||
void findByFluentExampleWithInterfaceBasedProjectionUsingSpEL() {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
User prototype = new User();
|
||||
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());
|
||||
|
||||
assertThat(users).extracting(UserProjectionUsingSpEL::hello)
|
||||
.contains(new GreetingsFrom().groot(firstUser.getFirstname()));
|
||||
}
|
||||
|
||||
@Test // GH-2294
|
||||
void findByFluentExampleWithSimplePropertyPathsDoesntLoadUnrequestedPaths() {
|
||||
|
||||
@@ -3364,4 +3384,10 @@ class UserRepositoryTests {
|
||||
private interface UserProjectionInterfaceBased {
|
||||
String getFirstname();
|
||||
}
|
||||
|
||||
private interface UserProjectionUsingSpEL {
|
||||
|
||||
@Value("#{@greetingsFrom.groot(target.firstname)}")
|
||||
String hello();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.mockito.quality.Strictness;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.data.jpa.repository.Lock;
|
||||
import org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor.CrudMethodMetadataPopulatingMethodInterceptor;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
@@ -56,7 +57,7 @@ class CrudMethodMetadataPopulatingMethodInterceptorUnitTests {
|
||||
|
||||
ProxyFactory factory = new ProxyFactory(new Object());
|
||||
factory.addInterface(Sample.class);
|
||||
factory.addAdvice(new CrudMethodMetadataPopulatingMethodInterceptor(information));
|
||||
factory.addAdvice(new CrudMethodMetadataPopulatingMethodInterceptor(information, SpelAwareProxyProjectionFactory::new));
|
||||
factory.addAdvice(new MethodInterceptor() {
|
||||
|
||||
@Override
|
||||
@@ -78,7 +79,7 @@ class CrudMethodMetadataPopulatingMethodInterceptorUnitTests {
|
||||
when(information.getRepositoryInterface()).thenReturn((Class) Sample.class);
|
||||
|
||||
CrudMethodMetadataPopulatingMethodInterceptor interceptor = new CrudMethodMetadataPopulatingMethodInterceptor(
|
||||
information);
|
||||
information, () -> new SpelAwareProxyProjectionFactory());
|
||||
interceptor.invoke(invocation);
|
||||
|
||||
assertThat(TransactionSynchronizationManager.getResource(method)).isNull();
|
||||
@@ -88,7 +89,7 @@ class CrudMethodMetadataPopulatingMethodInterceptorUnitTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
void looksUpCrudMethodMetadataForEveryInvocation() {
|
||||
|
||||
CrudMethodMetadata metadata = new CrudMethodMetadataPostProcessor().getCrudMethodMetadata();
|
||||
CrudMethodMetadata metadata = new CrudMethodMetadataPostProcessor(() -> new SpelAwareProxyProjectionFactory()).getCrudMethodMetadata();
|
||||
when(information.isQueryMethod(any())).thenReturn(false);
|
||||
when(information.getRepositoryInterface()).thenReturn((Class) Sample.class);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class FetchableFluentQueryByPredicateUnitTests {
|
||||
Sort s1 = Sort.by(Order.by("s1"));
|
||||
Sort s2 = Sort.by(Order.by("s2"));
|
||||
FetchableFluentQueryByPredicate f = new FetchableFluentQueryByPredicate(null, null, null, null, null, null, null,
|
||||
null);
|
||||
null, null);
|
||||
f = (FetchableFluentQueryByPredicate) f.sortBy(s1).sortBy(s2);
|
||||
assertThat(f.sort).isEqualTo(s1.and(s2));
|
||||
}
|
||||
|
||||
@@ -43,4 +43,6 @@
|
||||
|
||||
<bean class="org.springframework.data.jpa.repository.support.EntityManagerBeanDefinitionRegistrarPostProcessor" />
|
||||
|
||||
<bean class="org.springframework.data.jpa.repository.GreetingsFrom" name="greetingsFrom" />
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -28,4 +28,6 @@
|
||||
<!-- Register custom DAO implementation explicitly -->
|
||||
<bean id="userRepositoryImpl" class="org.springframework.data.jpa.repository.sample.UserRepositoryImpl" />
|
||||
|
||||
<bean class="org.springframework.data.jpa.repository.GreetingsFrom" name="greetingsFrom" />
|
||||
|
||||
</beans>
|
||||
|
||||
Reference in New Issue
Block a user