diff --git a/pom.xml b/pom.xml index dd336ca646..b2b487ac99 100755 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-jpa-parent - 3.3.0-SNAPSHOT + 3.3.x-3410-SNAPSHOT pom Spring Data JPA Parent diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml index 470678d048..7bb04f9ed5 100755 --- a/spring-data-envers/pom.xml +++ b/spring-data-envers/pom.xml @@ -5,12 +5,12 @@ org.springframework.data spring-data-envers - 3.3.0-SNAPSHOT + 3.3.x-3410-SNAPSHOT org.springframework.data spring-data-jpa-parent - 3.3.0-SNAPSHOT + 3.3.x-3410-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml index 6bd074181c..9d2eed1835 100644 --- a/spring-data-jpa-distribution/pom.xml +++ b/spring-data-jpa-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-jpa-parent - 3.3.0-SNAPSHOT + 3.3.x-3410-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml index 67ad4cabb2..f6138be568 100644 --- a/spring-data-jpa/pom.xml +++ b/spring-data-jpa/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jpa - 3.3.0-SNAPSHOT + 3.3.x-3410-SNAPSHOT Spring Data JPA Spring Data module for JPA repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-jpa-parent - 3.3.0-SNAPSHOT + 3.3.x-3410-SNAPSHOT ../pom.xml diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java index b6cff2cd28..ae738974b1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java @@ -85,4 +85,5 @@ public interface CrudMethodMetadata { * @since 1.9 */ Method getMethod(); + } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java index 9daa2377be..246e82dcd6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java @@ -28,6 +28,7 @@ import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; + import org.springframework.aop.TargetSource; import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.factory.BeanClassLoaderAware; @@ -274,6 +275,7 @@ public Optional getEntityGraph() { public Method getMethod() { return method; } + } private static class ThreadBoundTargetSource implements TargetSource { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java index b6e51c2904..9ed0a0ce3e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -34,6 +34,7 @@ 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 @@ * @author Mark Paluch * @author Jens Schauder * @author J.R. Onyschak + * @author Christoph Strobl * @since 2.6 */ class FetchableFluentQueryByPredicate extends FluentQuerySupport implements FetchableFluentQuery { @@ -64,21 +66,21 @@ class FetchableFluentQueryByPredicate extends FluentQuerySupport imp private final Function existsOperation; private final EntityManager entityManager; - public FetchableFluentQueryByPredicate(Predicate predicate, Class entityType, + FetchableFluentQueryByPredicate(Predicate predicate, Class entityType, Function> finder, PredicateScrollDelegate scroll, BiFunction> pagedFinder, Function countOperation, - Function existsOperation, EntityManager entityManager) { + Function existsOperation, EntityManager entityManager, ProjectionFactory projectionFactory) { this(predicate, entityType, (Class) entityType, Sort.unsorted(), 0, Collections.emptySet(), finder, scroll, - pagedFinder, countOperation, existsOperation, entityManager); + pagedFinder, countOperation, existsOperation, entityManager, projectionFactory); } private FetchableFluentQueryByPredicate(Predicate predicate, Class entityType, Class resultType, Sort sort, int limit, Collection properties, Function> finder, PredicateScrollDelegate scroll, BiFunction> pagedFinder, Function countOperation, Function 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,7 @@ public FetchableFluentQuery sortBy(Sort sort) { 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, projectionFactory); } @Override @@ -103,7 +105,7 @@ public FetchableFluentQuery limit(int limit) { 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, projectionFactory); } @Override @@ -116,14 +118,15 @@ public FetchableFluentQuery as(Class resultType) { } return new FetchableFluentQueryByPredicate<>(predicate, entityType, resultType, sort, limit, properties, finder, - scroll, pagedFinder, countOperation, existsOperation, entityManager); + scroll, pagedFinder, countOperation, existsOperation, entityManager, projectionFactory); } @Override public FetchableFluentQuery project(Collection 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, + projectionFactory); } @Override @@ -230,7 +233,6 @@ private Function getConversionFunction() { return getConversionFunction(entityType, resultType); } - static class PredicateScrollDelegate extends ScrollDelegate { private final ScrollQueryFactory scrollFunction; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java index 57bec8597e..6121be4374 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryBySpecification.java @@ -36,6 +36,7 @@ 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 @@ * @param Domain type * @param Result type * @author Greg Turnquist + * @author Christoph Strobl * @since 3.0 */ class FetchableFluentQueryBySpecification extends FluentQuerySupport @@ -59,20 +61,21 @@ class FetchableFluentQueryBySpecification extends FluentQuerySupport private final Function, Boolean> existsOperation; private final EntityManager entityManager; - public FetchableFluentQueryBySpecification(Specification spec, Class entityType, - Function> finder, SpecificationScrollDelegate scrollDelegate, - Function, Long> countOperation, Function, Boolean> existsOperation, - EntityManager entityManager) { + FetchableFluentQueryBySpecification(Specification spec, Class entityType, Function> finder, + SpecificationScrollDelegate scrollDelegate, Function, Long> countOperation, + Function, Boolean> existsOperation, EntityManager entityManager, + ProjectionFactory projectionFactory) { this(spec, entityType, (Class) entityType, Sort.unsorted(), 0, Collections.emptySet(), finder, scrollDelegate, - countOperation, existsOperation, entityManager); + countOperation, existsOperation, entityManager, projectionFactory); } private FetchableFluentQueryBySpecification(Specification spec, Class entityType, Class resultType, Sort sort, int limit, Collection properties, Function> finder, SpecificationScrollDelegate scrollDelegate, Function, Long> countOperation, - Function, Boolean> existsOperation, EntityManager entityManager) { + Function, 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 @@ public FetchableFluentQuery sortBy(Sort sort) { 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, projectionFactory); } @Override @@ -96,7 +99,7 @@ public FetchableFluentQuery limit(int limit) { 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, projectionFactory); } @Override @@ -108,14 +111,14 @@ public FetchableFluentQuery as(Class resultType) { } return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, sort, limit, properties, finder, - scroll, countOperation, existsOperation, entityManager); + scroll, countOperation, existsOperation, entityManager, projectionFactory); } @Override public FetchableFluentQuery project(Collection properties) { return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, sort, limit, properties, finder, - scroll, countOperation, existsOperation, entityManager); + scroll, countOperation, existsOperation, entityManager, projectionFactory); } @Override diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java index 4cc28c3df2..5917a119f5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FluentQuerySupport.java @@ -26,7 +26,7 @@ import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; -import org.springframework.data.projection.SpelAwareProxyProjectionFactory; +import org.springframework.data.projection.ProjectionFactory; import org.springframework.lang.Nullable; /** @@ -36,6 +36,7 @@ * @author Greg Turnquist * @author Jens Schauder * @author Mark Paluch + * @author Christoph Strobl * @since 2.6 */ abstract class FluentQuerySupport { @@ -45,11 +46,10 @@ abstract class FluentQuerySupport { protected final int limit; protected final Set properties; protected final Class entityType; - - private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory(); + protected final ProjectionFactory projectionFactory; FluentQuerySupport(Class resultType, Sort sort, int limit, @Nullable Collection properties, - Class entityType) { + Class entityType, ProjectionFactory projectionFactory) { this.resultType = resultType; this.sort = sort; @@ -62,6 +62,7 @@ abstract class FluentQuerySupport { } this.entityType = entityType; + this.projectionFactory = projectionFactory; } final Collection mergeProperties(Collection additionalProperties) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryConfigurationAware.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryConfigurationAware.java new file mode 100644 index 0000000000..942e209a10 --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryConfigurationAware.java @@ -0,0 +1,55 @@ +/* + * 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.support; + +import org.springframework.data.jpa.repository.query.EscapeCharacter; +import org.springframework.data.projection.ProjectionFactory; + +/** + * Interface to be implemented by classes that want to be aware of their configuration in a JPA repository context. + * + * @author Mark Paluch + * @since 3.2.6 + */ +public interface JpaRepositoryConfigurationAware { + + /** + * Configures the {@link EscapeCharacter} to be used with the repository. + * + * @param escapeCharacter must not be {@literal null}. + */ + default void setEscapeCharacter(EscapeCharacter escapeCharacter) { + + } + + /** + * Configures the {@link ProjectionFactory} to be used with the repository. + * + * @param projectionFactory must not be {@literal null}. + */ + default void setProjectionFactory(ProjectionFactory projectionFactory) { + + } + + /** + * Configures the {@link CrudMethodMetadata} to be used with the repository. + * + * @param metadata must not be {@literal null}. + */ + default void setRepositoryMethodMetadata(CrudMethodMetadata metadata) { + + } +} diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index a0ec78a0c7..69ef36ee65 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -83,6 +83,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { private final EntityManager entityManager; private final QueryExtractor extractor; private final CrudMethodMetadataPostProcessor crudMethodMetadataPostProcessor; + private final CrudMethodMetadata crudMethodMetadata; private EntityPathResolver entityPathResolver; private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT; @@ -116,6 +117,8 @@ public JpaRepositoryFactory(EntityManager entityManager) { if (extractor.equals(PersistenceProvider.ECLIPSELINK)) { addQueryCreationListener(new EclipseLinkProjectionQueryCreationListener(entityManager)); } + + this.crudMethodMetadata = crudMethodMetadataPostProcessor.getCrudMethodMetadata(); } @Override @@ -192,12 +195,13 @@ public void setQueryRewriterProvider(QueryRewriterProvider queryRewriterProvider protected final JpaRepositoryImplementation getTargetRepository(RepositoryInformation information) { JpaRepositoryImplementation repository = getTargetRepository(information, entityManager); - repository.setRepositoryMethodMetadata(crudMethodMetadataPostProcessor.getCrudMethodMetadata()); - repository.setEscapeCharacter(escapeCharacter); + + invokeAwareMethods(repository); return repository; } + /** * Callback to create a {@link JpaRepository} instance with the given {@link EntityManager} * @@ -250,7 +254,7 @@ public JpaEntityInformation getEntityInformation(Class domainC protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) { return getRepositoryFragments(metadata, entityManager, entityPathResolver, - crudMethodMetadataPostProcessor.getCrudMethodMetadata()); + this.crudMethodMetadata); } /** @@ -279,13 +283,23 @@ protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata "Cannot combine Querydsl and reactive repository support in a single interface"); } - return RepositoryFragments.just(new QuerydslJpaPredicateExecutor<>(getEntityInformation(metadata.getDomainType()), - entityManager, resolver, crudMethodMetadata)); + QuerydslJpaPredicateExecutor querydslJpaPredicateExecutor = new QuerydslJpaPredicateExecutor<>( + getEntityInformation(metadata.getDomainType()), entityManager, resolver, crudMethodMetadata); + invokeAwareMethods(querydslJpaPredicateExecutor); + + return RepositoryFragments.just(querydslJpaPredicateExecutor); } return RepositoryFragments.empty(); } + private void invokeAwareMethods(JpaRepositoryConfigurationAware repository) { + + repository.setRepositoryMethodMetadata(crudMethodMetadata); + repository.setEscapeCharacter(escapeCharacter); + repository.setProjectionFactory(getProjectionFactory()); + } + private static boolean isTransactionNeeded(Class repositoryClass) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(repositoryClass); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java index 9984675ac4..d3a5b1c5fe 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryImplementation.java @@ -17,7 +17,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.data.jpa.repository.query.EscapeCharacter; import org.springframework.data.repository.NoRepositoryBean; /** @@ -28,21 +27,7 @@ * @author Jens Schauder */ @NoRepositoryBean -public interface JpaRepositoryImplementation extends JpaRepository, JpaSpecificationExecutor { +public interface JpaRepositoryImplementation + extends JpaRepository, JpaSpecificationExecutor, JpaRepositoryConfigurationAware { - /** - * Configures the {@link CrudMethodMetadata} to be used with the repository. - * - * @param crudMethodMetadata must not be {@literal null}. - */ - void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata); - - /** - * Configures the {@link EscapeCharacter} to be used with the repository. - * - * @param escapeCharacter Must not be {@literal null}. - */ - default void setEscapeCharacter(EscapeCharacter escapeCharacter) { - - } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java index 606d56631e..c16f95c0a1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutor.java @@ -36,6 +36,8 @@ 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; @@ -71,14 +73,15 @@ * @author Greg Turnquist * @author Yanming Zhou */ -public class QuerydslJpaPredicateExecutor implements QuerydslPredicateExecutor { +public class QuerydslJpaPredicateExecutor implements QuerydslPredicateExecutor, JpaRepositoryConfigurationAware { private final JpaEntityInformation entityInformation; private final EntityPath path; private final Querydsl querydsl; private final QuerydslQueryStrategy scrollQueryAdapter; private final EntityManager entityManager; - private final CrudMethodMetadata metadata; + private @Nullable CrudMethodMetadata metadata; + private @Nullable ProjectionFactory projectionFactory; /** * Creates a new {@link QuerydslJpaPredicateExecutor} from the given domain class and {@link EntityManager} and uses @@ -100,6 +103,16 @@ public QuerydslJpaPredicateExecutor(JpaEntityInformation entityInformation this.scrollQueryAdapter = new QuerydslQueryStrategy(); } + @Override + public void setRepositoryMethodMetadata(CrudMethodMetadata metadata) { + this.metadata = metadata; + } + + @Override + public void setProjectionFactory(ProjectionFactory projectionFactory) { + this.projectionFactory = projectionFactory; + } + @Override public Optional findOne(Predicate predicate) { @@ -196,7 +209,7 @@ public R findBy(Predicate predicate, Function) querydsl.applySorting(sort, select); if (scrollPosition instanceof OffsetScrollPosition offset) { - if(!offset.isInitial()) { + if (!offset.isInitial()) { select.offset(offset.getOffset() + 1); } } @@ -223,8 +236,8 @@ public R findBy(Predicate predicate, Function) fluentQuery); } @@ -251,7 +264,6 @@ public boolean exists(Predicate predicate) { AbstractJPAQuery query = doCreateQuery(getQueryHints().withFetchGraphs(entityManager), predicate); CrudMethodMetadata metadata = getRepositoryMethodMetadata(); - if (metadata == null) { return query; } @@ -331,6 +343,15 @@ private List executeSorted(JPQLQuery query, Sort sort) { return querydsl.applySorting(sort, query).fetch(); } + private ProjectionFactory getProjectionFactory() { + + if (projectionFactory == null) { + projectionFactory = new SpelAwareProxyProjectionFactory(); + } + + return projectionFactory; + } + class QuerydslQueryStrategy implements QueryStrategy, BooleanExpression> { @Override diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 879e683865..50afeee8a5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -61,6 +61,8 @@ 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; @@ -103,6 +105,7 @@ public class SimpleJpaRepository implements JpaRepositoryImplementation domainClass, EntityManager entityManager) { * Configures a custom {@link CrudMethodMetadata} to be used to detect {@link LockModeType}s and query hints to be * applied to queries. * - * @param crudMethodMetadata + * @param metadata */ @Override - public void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) { - this.metadata = crudMethodMetadata; + public void setRepositoryMethodMetadata(CrudMethodMetadata metadata) { + this.metadata = metadata; } @Override @@ -147,6 +150,11 @@ public void setEscapeCharacter(EscapeCharacter escapeCharacter) { this.escapeCharacter = escapeCharacter; } + @Override + public void setProjectionFactory(ProjectionFactory projectionFactory) { + this.projectionFactory = projectionFactory; + } + @Nullable protected CrudMethodMetadata getRepositoryMethodMetadata() { return metadata; @@ -524,8 +532,8 @@ private R doFindBy(Specification spec, Class domainClass, SpecificationScrollDelegate scrollDelegate = new SpecificationScrollDelegate<>(scrollFunction, entityInformation); - FetchableFluentQuery fluentQuery = new FetchableFluentQueryBySpecification<>(spec, domainClass, finder, - scrollDelegate, this::count, this::exists, this.entityManager); + FetchableFluentQueryBySpecification fluentQuery = new FetchableFluentQueryBySpecification<>(spec, domainClass, finder, + scrollDelegate, this::count, this::exists, this.entityManager, getProjectionFactory()); return queryFunction.apply((FetchableFluentQuery) fluentQuery); } @@ -903,6 +911,15 @@ private void applyComment(CrudMethodMetadata metadata, BiConsumer 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 @@ private Page executeSpecWithSort(Sort sort) { private interface UserProjectionInterfaceBased { String getFirstname(); } + + private interface UserProjectionUsingSpEL { + + @Value("#{@greetingsFrom.groot(target.firstname)}") + String hello(); + } } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java index 143a3c15da..c9bcee59a3 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPopulatingMethodInterceptorUnitTests.java @@ -18,10 +18,10 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import java.lang.reflect.Method; - import jakarta.persistence.LockModeType; +import java.lang.reflect.Method; + import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.junit.jupiter.api.Test; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java index 767ac14cbe..f2c5e9f00c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicateUnitTests.java @@ -35,7 +35,7 @@ void multipleSortBy() { 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)); } diff --git a/spring-data-jpa/src/test/resources/application-context.xml b/spring-data-jpa/src/test/resources/application-context.xml index 74529a5212..1bd58b22cd 100644 --- a/spring-data-jpa/src/test/resources/application-context.xml +++ b/spring-data-jpa/src/test/resources/application-context.xml @@ -43,4 +43,6 @@ + + diff --git a/spring-data-jpa/src/test/resources/config/namespace-application-context.xml b/spring-data-jpa/src/test/resources/config/namespace-application-context.xml index e192050ec6..b5c02d868d 100644 --- a/spring-data-jpa/src/test/resources/config/namespace-application-context.xml +++ b/spring-data-jpa/src/test/resources/config/namespace-application-context.xml @@ -28,4 +28,6 @@ + +