diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/AotMetamodel.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotMetamodel.java similarity index 97% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/AotMetamodel.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotMetamodel.java index fcdd221cf..8b68214ab 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/AotMetamodel.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotMetamodel.java @@ -1,5 +1,5 @@ /* - * Copyright 2024-2025 the original author or authors. + * Copyright 2025 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/AotQueries.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotQueries.java similarity index 87% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/AotQueries.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotQueries.java index 14f94e625..0b900c72a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/AotQueries.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotQueries.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; -import jakarta.validation.constraints.Null; import java.util.function.Function; +import org.jspecify.annotations.Nullable; + import org.springframework.data.jpa.repository.query.DeclaredQuery; import org.springframework.data.jpa.repository.query.QueryEnhancer; import org.springframework.data.jpa.repository.query.QueryEnhancerSelector; @@ -35,7 +36,8 @@ record AotQueries(AotQuery result, AotQuery count) { /** * Derive a count query from the given query. */ - public static AotQueries from(StringAotQuery query, @Null String countProjection, QueryEnhancerSelector selector) { + public static AotQueries from(StringAotQuery query, @Nullable String countProjection, + QueryEnhancerSelector selector) { return from(query, StringAotQuery::getQuery, countProjection, selector); } @@ -43,7 +45,7 @@ record AotQueries(AotQuery result, AotQuery count) { * Derive a count query from the given query. */ public static AotQueries from(T query, Function queryMapper, - @Null String countProjection, QueryEnhancerSelector selector) { + @Nullable String countProjection, QueryEnhancerSelector selector) { DeclaredQuery underlyingQuery = queryMapper.apply(query); QueryEnhancer queryEnhancer = selector.select(underlyingQuery).create(underlyingQuery); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/AotQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotQuery.java similarity index 97% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/AotQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotQuery.java index 926fe45c4..b9b3eeb1a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/AotQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotQuery.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; import java.util.List; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/AotRepositoryFragmentSupport.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotRepositoryFragmentSupport.java similarity index 81% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/AotRepositoryFragmentSupport.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotRepositoryFragmentSupport.java index a20acf49f..8c0abf97a 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/AotRepositoryFragmentSupport.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotRepositoryFragmentSupport.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; import java.lang.reflect.Method; @@ -32,6 +32,7 @@ import org.springframework.data.repository.core.support.RepositoryFactoryBeanSup import org.springframework.data.repository.query.ParametersSource; import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.repository.query.ValueExpressionDelegate; +import org.springframework.data.util.Lazy; import org.springframework.util.ConcurrentLruCache; /** @@ -48,11 +49,11 @@ public class AotRepositoryFragmentSupport { private final ProjectionFactory projectionFactory; - private final ConcurrentLruCache enhancers; + private final Lazy> enhancers; - private final ConcurrentLruCache expressions; + private final Lazy> expressions; - private final ConcurrentLruCache contextProviders; + private final Lazy> contextProviders; protected AotRepositoryFragmentSupport(QueryEnhancerSelector selector, RepositoryFactoryBeanSupport.FragmentCreationContext context) { @@ -66,10 +67,10 @@ public class AotRepositoryFragmentSupport { this.repositoryMetadata = repositoryMetadata; this.valueExpressions = valueExpressions; this.projectionFactory = projectionFactory; - this.enhancers = new ConcurrentLruCache<>(32, query -> selector.select(query).create(query)); - this.expressions = new ConcurrentLruCache<>(32, valueExpressions::parse); - this.contextProviders = new ConcurrentLruCache<>(32, it -> valueExpressions - .createValueContextProvider(new JpaParameters(ParametersSource.of(repositoryMetadata, it)))); + this.enhancers = Lazy.of(() -> new ConcurrentLruCache<>(32, query -> selector.select(query).create(query))); + this.expressions = Lazy.of(() -> new ConcurrentLruCache<>(32, valueExpressions::parse)); + this.contextProviders = Lazy.of(() -> new ConcurrentLruCache<>(32, it -> valueExpressions + .createValueContextProvider(new JpaParameters(ParametersSource.of(repositoryMetadata, it))))); } /** @@ -82,7 +83,7 @@ public class AotRepositoryFragmentSupport { */ protected String rewriteQuery(DeclaredQuery query, Sort sort, Class returnedType) { - QueryEnhancer queryStringEnhancer = this.enhancers.get(query); + QueryEnhancer queryStringEnhancer = this.enhancers.get().get(query); return queryStringEnhancer.rewrite(new DefaultQueryRewriteInformation(sort, ReturnedType.of(returnedType, repositoryMetadata.getDomainType(), projectionFactory))); } @@ -97,8 +98,8 @@ public class AotRepositoryFragmentSupport { */ protected @Nullable Object evaluateExpression(Method method, String expressionString, Object... args) { - ValueExpression expression = this.expressions.get(expressionString); - ValueEvaluationContextProvider contextProvider = this.contextProviders.get(method); + ValueExpression expression = this.expressions.get().get(expressionString); + ValueEvaluationContextProvider contextProvider = this.contextProviders.get().get(method); return expression.evaluate(contextProvider.getEvaluationContext(args, expression.getExpressionDependencies())); } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/JpaCodeBlocks.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaCodeBlocks.java similarity index 96% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/JpaCodeBlocks.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaCodeBlocks.java index e39e89327..75e74a78e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/JpaCodeBlocks.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaCodeBlocks.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; @@ -55,7 +55,6 @@ import org.springframework.util.StringUtils; class JpaCodeBlocks { /** - * @param context * @return new {@link QueryBlockBuilder}. */ public static QueryBlockBuilder queryBuilder(AotQueryMethodGenerationContext context, JpaQueryMethod queryMethod) { @@ -63,7 +62,6 @@ class JpaCodeBlocks { } /** - * @param context * @return new {@link QueryExecutionBlockBuilder}. */ static QueryExecutionBlockBuilder executionBuilder(AotQueryMethodGenerationContext context, @@ -79,9 +77,8 @@ class JpaCodeBlocks { private final AotQueryMethodGenerationContext context; private final JpaQueryMethod queryMethod; private String queryVariableName = "query"; - private AotQueries queries; + private @Nullable AotQueries queries; private MergedAnnotation queryHints = MergedAnnotation.missing(); - private MergedAnnotation query = MergedAnnotation.missing(); private @Nullable String sqlResultSetMapping; private @Nullable Class queryReturnType; @@ -101,18 +98,6 @@ class JpaCodeBlocks { return this; } - public QueryBlockBuilder queryHints(MergedAnnotation queryHints) { - - this.queryHints = queryHints; - return this; - } - - public QueryBlockBuilder query(MergedAnnotation query) { - - this.query = query; - return this; - } - public QueryBlockBuilder nativeQuery(MergedAnnotation nativeQuery) { if (nativeQuery.isPresent()) { @@ -121,6 +106,12 @@ class JpaCodeBlocks { return this; } + public QueryBlockBuilder queryHints(MergedAnnotation queryHints) { + + this.queryHints = queryHints; + return this; + } + public QueryBlockBuilder queryReturnType(@Nullable Class queryReturnType) { this.queryReturnType = queryReturnType; return this; @@ -142,7 +133,7 @@ class JpaCodeBlocks { String queryStringNameVariableName = null; - if (queries.result() instanceof StringAotQuery sq) { + if (queries != null && queries.result() instanceof StringAotQuery sq) { queryStringNameVariableName = "%sString".formatted(queryVariableName); builder.addStatement("$T $L = $S", String.class, queryStringNameVariableName, sq.getQueryString()); @@ -157,9 +148,6 @@ class JpaCodeBlocks { builder.addStatement("$T $L = $S", String.class, countQueryStringNameVariableName, sq.getQueryString()); } - // sorting - // TODO: refactor into sort builder - String sortParameterName = context.getSortParameterName(); if (sortParameterName == null && context.getPageableParameterName() != null) { sortParameterName = "%s.getSort()".formatted(context.getPageableParameterName()); @@ -202,7 +190,7 @@ class JpaCodeBlocks { builder.beginControlFlow("if ($L.isSorted())", sort); builder.addStatement("$T declaredQuery = $T.$L($L)", DeclaredQuery.class, DeclaredQuery.class, - queries.isNative() ? "nativeQuery" : "jpqlQuery", + queries != null && queries.isNative() ? "nativeQuery" : "jpqlQuery", queryString); builder.addStatement("$L = rewriteQuery(declaredQuery, $L, $T.class)", queryString, sort, actualReturnType); @@ -227,7 +215,7 @@ class JpaCodeBlocks { builder.beginControlFlow("if ($L.isLimited())", limit); builder.addStatement("$L.setMaxResults($L.max())", queryVariableName, limit); builder.endControlFlow(); - } else if (queries.result().isLimited()) { + } else if (queries != null && queries.result().isLimited()) { builder.addStatement("$L.setMaxResults($L)", queryVariableName, queries.result().getLimit().max()); } @@ -358,7 +346,7 @@ class JpaCodeBlocks { Builder builder = CodeBlock.builder(); ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer(); - String[] parameterNames = discoverer.getParameterNames(context.getMethod()); + var parameterNames = discoverer.getParameterNames(context.getMethod()); String expressionString = expr.expression().getExpressionString(); // re-wrap expression diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/JpaRepositoryContributor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributor.java similarity index 89% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/JpaRepositoryContributor.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributor.java index 0b2522ce2..1cacad653 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/JpaRepositoryContributor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributor.java @@ -1,5 +1,5 @@ /* - * Copyright 2024-2025 the original author or authors. + * Copyright 2025 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. @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.Tuple; import jakarta.persistence.TypedQueryReference; +import jakarta.persistence.metamodel.Metamodel; import java.lang.reflect.Method; import java.util.Arrays; @@ -57,6 +58,7 @@ import org.springframework.data.repository.core.support.RepositoryFactoryBeanSup import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.data.util.TypeInformation; import org.springframework.javapoet.CodeBlock; import org.springframework.javapoet.TypeName; import org.springframework.javapoet.TypeSpec; @@ -76,13 +78,23 @@ import org.springframework.util.StringUtils; */ public class JpaRepositoryContributor extends RepositoryContributor { - private final AotMetamodel metaModel; + private final EntityManagerFactory emf; + private final Metamodel metaModel; private final PersistenceProvider persistenceProvider; public JpaRepositoryContributor(AotRepositoryContext repositoryContext) { super(repositoryContext); - this.metaModel = new AotMetamodel(repositoryContext.getResolvedTypes()); - this.persistenceProvider = PersistenceProvider.fromEntityManagerFactory(metaModel.getEntityManagerFactory()); + AotMetamodel amm = new AotMetamodel(repositoryContext.getResolvedTypes()); + this.metaModel = amm; + this.emf = amm.getEntityManagerFactory(); + this.persistenceProvider = PersistenceProvider.fromEntityManagerFactory(amm.getEntityManagerFactory()); + } + + public JpaRepositoryContributor(AotRepositoryContext repositoryContext, EntityManagerFactory entityManagerFactory) { + super(repositoryContext); + this.emf = entityManagerFactory; + this.metaModel = entityManagerFactory.getMetamodel(); + this.persistenceProvider = PersistenceProvider.fromEntityManagerFactory(entityManagerFactory); } @Override @@ -118,6 +130,17 @@ public class JpaRepositoryContributor extends RepositoryContributor { return null; } + ReturnedType returnedType = queryMethod.getResultProcessor().getReturnedType(); + + // no interface/dynamic projections for now. + if (returnedType.isProjecting() && returnedType.getReturnedType().isInterface()) { + return null; + } + + if (queryMethod.getParameters().hasDynamicProjection()) { + return null; + } + // no KeysetScrolling for now. if (queryMethod.getParameters().hasScrollPositionParameter()) { return null; @@ -125,9 +148,13 @@ public class JpaRepositoryContributor extends RepositoryContributor { if (queryMethod.isModifyingQuery()) { - Class returnType = repositoryInformation.getReturnType(method).getType(); - if (!ClassUtils.isVoidType(returnType) - && !JpaCodeBlocks.QueryExecutionBlockBuilder.returnsModifying(returnType)) { + TypeInformation returnType = repositoryInformation.getReturnType(method); + + boolean returnsCount = JpaCodeBlocks.QueryExecutionBlockBuilder.returnsModifying(returnType.getType()); + + boolean isVoid = ClassUtils.isVoidType(returnType.getType()); + + if (!returnsCount && !isVoid) { return null; } } @@ -140,15 +167,14 @@ public class JpaRepositoryContributor extends RepositoryContributor { MergedAnnotation nativeQuery = context.getAnnotation(NativeQuery.class); MergedAnnotation queryHints = context.getAnnotation(QueryHints.class); MergedAnnotation modifying = context.getAnnotation(Modifying.class); - ReturnedType returnedType = context.getReturnedType(); body.add(context.codeBlocks().logDebug("invoking [%s]".formatted(context.getMethod().getName()))); AotQueries aotQueries = getQueries(context, query, selector, queryMethod, returnedType); body.add(JpaCodeBlocks.queryBuilder(context, queryMethod).filter(aotQueries) - .queryReturnType(getQueryReturnType(aotQueries.result(), returnedType, context)).query(query) - .nativeQuery(nativeQuery).queryHints(queryHints).build()); + .queryReturnType(getQueryReturnType(aotQueries.result(), returnedType, context)).nativeQuery(nativeQuery) + .queryHints(queryHints).build()); body.add( JpaCodeBlocks.executionBuilder(context, queryMethod).modifying(modifying).query(aotQueries.result()).build()); @@ -178,8 +204,7 @@ public class JpaRepositoryContributor extends RepositoryContributor { UnaryOperator operator = s -> s.replaceAll("#\\{#entityName}", domainType.getName()); boolean isNative = query.getBoolean("nativeQuery"); - Function queryFunction = isNative ? StringAotQuery::nativeQuery - : StringAotQuery::jpqlQuery; + Function queryFunction = isNative ? StringAotQuery::nativeQuery : StringAotQuery::jpqlQuery; queryFunction = operator.andThen(queryFunction); String queryString = query.getString("value"); @@ -252,8 +277,6 @@ public class JpaRepositoryContributor extends RepositoryContributor { returnedType.getReturnedType(), returnedType.getTypeToRead(), void.class, null, Long.class, Integer.class, Long.TYPE, Integer.TYPE, Number.class); - EntityManagerFactory emf = metaModel.getEntityManagerFactory(); - for (Class candidate : candidates) { Map> namedQueries = emf.getNamedQueries(candidate); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/NamedAotQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/NamedAotQuery.java similarity index 96% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/NamedAotQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/NamedAotQuery.java index 4df1c509c..3f7b9293b 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/NamedAotQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/NamedAotQuery.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; import java.util.List; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/StringAotQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/StringAotQuery.java similarity index 98% rename from spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/StringAotQuery.java rename to spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/StringAotQuery.java index d68daa32b..499f1d6c6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/generated/StringAotQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/StringAotQuery.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; import java.util.List; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/package-info.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/package-info.java new file mode 100644 index 000000000..a0fa7b10f --- /dev/null +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/package-info.java @@ -0,0 +1,5 @@ +/** + * Ahead-of-Time (AOT) generation for Spring Data JPA repositories. + */ +@org.jspecify.annotations.NullMarked +package org.springframework.data.jpa.repository.aot; diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index 742387add..7de820f3e 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -52,7 +52,7 @@ import org.springframework.dao.DataAccessException; import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; import org.springframework.data.aot.AotContext; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.aot.generated.JpaRepositoryContributor; +import org.springframework.data.jpa.repository.aot.JpaRepositoryContributor; import org.springframework.data.jpa.repository.support.DefaultJpaContext; import org.springframework.data.jpa.repository.support.EntityManagerBeanDefinitionRegistrarPostProcessor; import org.springframework.data.jpa.repository.support.JpaEvaluationContextExtension; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/AotFragmentTestConfigurationSupport.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/AotFragmentTestConfigurationSupport.java similarity index 98% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/AotFragmentTestConfigurationSupport.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/AotFragmentTestConfigurationSupport.java index 3cecfff00..670c871ca 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/AotFragmentTestConfigurationSupport.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/AotFragmentTestConfigurationSupport.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/JpaRepositoryContributorIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributorIntegrationTests.java similarity index 97% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/JpaRepositoryContributorIntegrationTests.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributorIntegrationTests.java index ed91fab36..d6e0edebb 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/JpaRepositoryContributorIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributorIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2025 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; import static org.assertj.core.api.Assertions.*; @@ -399,9 +399,10 @@ class JpaRepositoryContributorIntegrationTests { assertThat(page.getContent()).extracting(UserDtoProjection::getEmailAddress).containsExactly("han@smuggler.net", "kylo@new-empire.com"); - // TODO Page noResults = fragment.findUserProjectionByLastnameStartingWith("a", PageRequest.of(0, 2, Sort.by("emailAddress"))); + + assertThat(noResults).isEmpty(); } // modifying @@ -419,6 +420,13 @@ class JpaRepositoryContributorIntegrationTests { assertThat(yodaShouldBeGone).isNull(); } + @Test + void shouldOmitAnnotatedDeleteReturningDomainType() { + + assertThatException().isThrownBy(() -> fragment.deleteAnnotatedQueryByEmailAddress("foo")) + .withRootCauseInstanceOf(NoSuchMethodException.class); + } + @Test void shouldApplyModifying() { @@ -456,11 +464,11 @@ class JpaRepositoryContributorIntegrationTests { void todo() { + // entity graphs // interface projections // dynamic projections // class type parameter - // entity graphs // synthetic parameters (keyset scrolling! yuck!) } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/StubRepositoryInformation.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/StubRepositoryInformation.java similarity index 96% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/StubRepositoryInformation.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/StubRepositoryInformation.java index e90ce0aae..6e9b1d900 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/StubRepositoryInformation.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/StubRepositoryInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2025 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; import java.lang.reflect.Method; import java.util.Set; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/TestJpaAotRepositoryContext.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/TestJpaAotRepositoryContext.java similarity index 96% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/TestJpaAotRepositoryContext.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/TestJpaAotRepositoryContext.java index df4e62a87..0aeaba364 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/TestJpaAotRepositoryContext.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/TestJpaAotRepositoryContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 the original author or authors. + * Copyright 2025 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; import jakarta.persistence.Entity; import jakarta.persistence.MappedSuperclass; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/UserDtoProjection.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/UserDtoProjection.java similarity index 94% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/UserDtoProjection.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/UserDtoProjection.java index bc8d8f578..3e8e97450 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/UserDtoProjection.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/UserDtoProjection.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; /** * @author Christoph Strobl diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/UserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/UserRepository.java similarity index 98% rename from spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/UserRepository.java rename to spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/UserRepository.java index 98c3ac7a3..9664faaea 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/generated/UserRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/UserRepository.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.jpa.repository.aot.generated; +package org.springframework.data.jpa.repository.aot; import jakarta.persistence.QueryHint; @@ -131,8 +131,7 @@ interface UserRepository extends CrudRepository { User deleteByEmailAddress(String username); - Long deleteReturningDeleteCountByEmailAddress(String username); - + // cannot generate delete and return a domain object @Modifying @Query("delete from User u where u.emailAddress = ?1") User deleteAnnotatedQueryByEmailAddress(String username); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java index 6d93f6ae9..e8907f16f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java @@ -24,6 +24,7 @@ import jakarta.persistence.Query; import jakarta.persistence.TypedQuery; import java.lang.reflect.Method; +import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.Optional; @@ -169,7 +170,7 @@ class JpaQueryExecutionUnitTests { @Test void modifyingExecutionRejectsNonIntegerOrVoidReturnType() { - when(method.getReturnType()).thenReturn((Class) Long.class); + when(method.getReturnType()).thenReturn((Class) BigDecimal.class); assertThatIllegalArgumentException().isThrownBy(() -> new ModifyingExecution(method, em)); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index 988e743cf..1833a1035 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -300,10 +300,6 @@ public interface UserRepository extends JpaRepository, JpaSpecifi // DATAJPA-460 List deleteByLastname(String lastname); - @Modifying - @Query("delete from User u where u.emailAddress = ?1") - User deleteAnnotatedQueryByEmailAddress(String username); - /** * Explicitly mapped to a procedure with name "plus1inout" in database. */