From fbd214f72efa062518baa54313c70af67c9afa79 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 9 Apr 2025 16:51:30 +0200 Subject: [PATCH] Polishing. Run AotMetamodel against live EntityManagerFactory, use Environment to check for AOT repository enabled flag. See #3830 --- .../data/jpa/repository/aot/AotMetamodel.java | 4 +- .../jpa/repository/aot/JpaCodeBlocks.java | 11 +- .../aot/JpaRepositoryContributor.java | 2 - .../config/JpaRepositoryConfigExtension.java | 21 +- .../data/jpa/repository/query/NamedQuery.java | 25 +- .../query/ParameterBindingParser.java | 427 ------------------ .../aot/TestJpaAotRepositoryContext.java | 9 + ...toryRegistrationAotProcessorUnitTests.java | 7 + .../query/DefaultEntityQueryUnitTests.java | 1 - 9 files changed, 60 insertions(+), 447 deletions(-) delete mode 100644 spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBindingParser.java diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotMetamodel.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotMetamodel.java index 8b68214ab..3c1ddd6e3 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotMetamodel.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotMetamodel.java @@ -96,7 +96,6 @@ class AotMetamodel implements Metamodel { return entityManager.get(); } - // TODO: Capture an existing factory bean (e.g. EntityManagerFactoryInfo) to extract PersistenceInfo public EntityManagerFactory getEntityManagerFactory() { return entityManagerFactory.get(); } @@ -125,7 +124,8 @@ class AotMetamodel implements Metamodel { public List getManagedClassNames() { return persistenceUnitInfo.getManagedClassNames(); } - }, Map.of("hibernate.dialect", "org.hibernate.dialect.H2Dialect")).build(); + }, Map.of("hibernate.dialect", "org.hibernate.dialect.H2Dialect", "hibernate.boot.allow_jdbc_metadata_access", + "false")).build(); } } diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaCodeBlocks.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaCodeBlocks.java index 5dacdd7cb..f9c9b45e6 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaCodeBlocks.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaCodeBlocks.java @@ -45,6 +45,7 @@ import org.springframework.data.support.PageableExecutionUtils; import org.springframework.javapoet.CodeBlock; import org.springframework.javapoet.CodeBlock.Builder; import org.springframework.javapoet.TypeName; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -140,6 +141,8 @@ class JpaCodeBlocks { */ public CodeBlock build() { + Assert.notNull(queries, "Queries must not be null"); + boolean isProjecting = context.getReturnedType().isProjecting(); Class actualReturnType = isProjecting ? context.getActualReturnType().toClass() : context.getRepositoryInformation().getDomainType(); @@ -153,7 +156,6 @@ class JpaCodeBlocks { builder.add("\n"); String queryStringVariableName = null; - String queryRewriterName = null; if (queries.result() instanceof StringAotQuery && queryRewriter != QueryRewriter.IdentityQueryRewriter.class) { @@ -162,7 +164,7 @@ class JpaCodeBlocks { builder.addStatement("$T $L = new $T()", queryRewriter, queryRewriterName, queryRewriter); } - if (queries != null && queries.result() instanceof StringAotQuery sq) { + if (queries.result() instanceof StringAotQuery sq) { queryStringVariableName = "%sString".formatted(queryVariableName); builder.add(buildQueryString(sq, queryStringVariableName)); @@ -183,7 +185,8 @@ class JpaCodeBlocks { } if ((StringUtils.hasText(sortParameterName) || StringUtils.hasText(dynamicReturnType)) - && queries.result() instanceof StringAotQuery) { + && queries != null && queries.result() instanceof StringAotQuery + && StringUtils.hasText(queryStringVariableName)) { builder.add(applyRewrite(sortParameterName, dynamicReturnType, queryStringVariableName, actualReturnType)); } @@ -605,7 +608,7 @@ class JpaCodeBlocks { } } else if (aotQuery != null && aotQuery.isExists()) { builder.addStatement("return !$L.getResultList().isEmpty()", queryVariableName); - } else { + } else if (aotQuery != null) { if (context.getReturnedType().isProjecting()) { diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributor.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributor.java index 785c9133c..54ae048b5 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributor.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributor.java @@ -189,8 +189,6 @@ public class JpaRepositoryContributor extends RepositoryContributor { MergedAnnotation entityGraph = context.getAnnotation(EntityGraph.class); MergedAnnotation modifying = context.getAnnotation(Modifying.class); - body.add(context.codeBlocks().logDebug("invoking [%s]".formatted(context.getMethod().getName()))); - AotEntityGraph aotEntityGraph = entityGraphLookup.findEntityGraph(entityGraph, repositoryInformation, returnedType, queryMethod); 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 7de820f3e..99eec5010 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 @@ -18,6 +18,7 @@ package org.springframework.data.jpa.repository.config; import static org.springframework.data.jpa.repository.config.BeanDefinitionNames.*; import jakarta.persistence.Entity; +import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.MappedSuperclass; import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceUnit; @@ -38,6 +39,7 @@ import org.jspecify.annotations.Nullable; import org.springframework.aot.generate.GenerationContext; import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -325,22 +327,29 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi */ public static class JpaRepositoryRegistrationAotProcessor extends RepositoryRegistrationAotProcessor { - protected RepositoryContributor contribute(AotRepositoryContext repositoryContext, GenerationContext generationContext) { + protected @Nullable RepositoryContributor contribute(AotRepositoryContext repositoryContext, + GenerationContext generationContext) { - // don't register domain types nor annotations. - - if (!AotContext.aotGeneratedRepositoriesEnabled()) { + boolean enabled = Boolean.parseBoolean( + repositoryContext.getEnvironment().getProperty(AotContext.GENERATED_REPOSITORIES_ENABLED, "false")); + if (!enabled) { return null; } - return new JpaRepositoryContributor(repositoryContext); + ConfigurableListableBeanFactory beanFactory = repositoryContext.getBeanFactory(); + EntityManagerFactory emf = beanFactory.getBeanProvider(EntityManagerFactory.class).getIfAvailable(); + + return emf != null ? new JpaRepositoryContributor(repositoryContext, emf) + : new JpaRepositoryContributor(repositoryContext); } @Nullable @Override + @SuppressWarnings("NullAway") protected RepositoryConfiguration getRepositoryMetadata(RegisteredBean bean) { RepositoryConfiguration configuration = super.getRepositoryMetadata(bean); - if (!configuration.getRepositoryBaseClassName().isEmpty()) { + + if (configuration != null && configuration.getRepositoryBaseClassName().isPresent()) { return configuration; } return new Meh<>(configuration); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index 5bf986d4b..a38bf9eaa 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -34,6 +34,7 @@ import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.util.Lazy; +import org.springframework.util.StringUtils; /** * Implementation of {@link RepositoryQuery} based on {@link jakarta.persistence.NamedQuery}s. @@ -97,12 +98,26 @@ final class NamedQuery extends AbstractJpaQuery { String queryString = extractor.extractQueryString(namedQuery); - // TODO: What is queryString is null? DeclaredQuery declaredQuery; - if (method.isNativeQuery() || (namedQuery != null && namedQuery.toString().contains("NativeQuery"))) { - declaredQuery = DeclaredQuery.nativeQuery(queryString); - } else { - declaredQuery = DeclaredQuery.jpqlQuery(queryString); + if (StringUtils.hasText(queryString)) { + if (method.isNativeQuery() || namedQuery.toString().contains("NativeQuery")) { + declaredQuery = DeclaredQuery.nativeQuery(queryString); + } else { + declaredQuery = DeclaredQuery.jpqlQuery(queryString); + } + } + else { + declaredQuery = new DeclaredQuery() { + @Override + public boolean isNative() { + return false; + } + + @Override + public String getQueryString() { + return ""; + } + }; } this.entityQuery = Lazy.of(() -> EntityQuery.create(declaredQuery, queryConfiguration.getSelector())); diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBindingParser.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBindingParser.java deleted file mode 100644 index 371016577..000000000 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBindingParser.java +++ /dev/null @@ -1,427 +0,0 @@ -/* - * 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. - * 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.query; - -import static java.util.regex.Pattern.CASE_INSENSITIVE; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.springframework.data.expression.ValueExpression; -import org.springframework.data.expression.ValueExpressionParser; -import org.springframework.data.jpa.repository.query.ParameterBinding.BindingIdentifier; -import org.springframework.data.jpa.repository.query.ParameterBinding.InParameterBinding; -import org.springframework.data.jpa.repository.query.ParameterBinding.LikeParameterBinding; -import org.springframework.data.jpa.repository.query.ParameterBinding.MethodInvocationArgument; -import org.springframework.data.jpa.repository.query.ParameterBinding.ParameterOrigin; -import org.springframework.data.repository.query.ValueExpressionQueryRewriter; -import org.springframework.data.repository.query.parser.Part.Type; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; - -/** - * A parser that extracts the parameter bindings from a given query string. - * - * @author Thomas Darimont - */ -public enum ParameterBindingParser { - - INSTANCE; - - private static final String EXPRESSION_PARAMETER_PREFIX = "__$synthetic$__"; - public static final String POSITIONAL_OR_INDEXED_PARAMETER = "\\?(\\d*+(?![#\\w]))"; - // .....................................................................^ not followed by a hash or a letter. - // .................................................................^ zero or more digits. - // .............................................................^ start with a question mark. - private static final Pattern PARAMETER_BINDING_BY_INDEX = Pattern.compile(POSITIONAL_OR_INDEXED_PARAMETER); - private static final Pattern PARAMETER_BINDING_PATTERN; - private static final Pattern JDBC_STYLE_PARAM = Pattern.compile("(?!\\\\)\\?(?!\\d)"); // no \ and [no digit] - private static final Pattern NUMBERED_STYLE_PARAM = Pattern.compile("(?!\\\\)\\?\\d"); // no \ and [digit] - private static final Pattern NAMED_STYLE_PARAM = Pattern.compile("(?!\\\\):\\w+"); // no \ and :[text] - - private static final String MESSAGE = "Already found parameter binding with same index / parameter name but differing binding type; " - + "Already have: %s, found %s; If you bind a parameter multiple times make sure they use the same binding"; - private static final int INDEXED_PARAMETER_GROUP = 4; - private static final int NAMED_PARAMETER_GROUP = 6; - private static final int COMPARISION_TYPE_GROUP = 1; - - public static class Metadata { - private boolean usesJdbcStyleParameters = false; - - public boolean usesJdbcStyleParameters() { - return usesJdbcStyleParameters; - } - } - - /** - * Utility to create unique parameter bindings for LIKE that refer to the same underlying method parameter but are - * bound to potentially unique query parameters for {@link LikeParameterBinding#prepare(Object) LIKE rewrite}. - * - * @author Mark Paluch - * @since 3.1.2 - */ - static class ParameterBindings { - - private final MultiValueMap methodArgumentToLikeBindings = new LinkedMultiValueMap<>(); - - private final Consumer registration; - private int syntheticParameterIndex; - - public ParameterBindings(List bindings, Consumer registration, - int syntheticParameterIndex) { - - for (ParameterBinding binding : bindings) { - this.methodArgumentToLikeBindings.put(binding.getIdentifier(), new ArrayList<>(List.of(binding))); - } - - this.registration = registration; - this.syntheticParameterIndex = syntheticParameterIndex; - } - - /** - * Return whether the identifier is already bound. - * - * @param identifier - * @return - */ - public boolean isBound(BindingIdentifier identifier) { - return !getBindings(identifier).isEmpty(); - } - - BindingIdentifier register(BindingIdentifier identifier, ParameterOrigin origin, - Function bindingFactory) { - - Assert.isInstanceOf(MethodInvocationArgument.class, origin); - - BindingIdentifier methodArgument = ((MethodInvocationArgument) origin).identifier(); - List bindingsForOrigin = getBindings(methodArgument); - - if (!isBound(identifier)) { - - ParameterBinding binding = bindingFactory.apply(identifier); - registration.accept(binding); - bindingsForOrigin.add(binding); - return binding.getIdentifier(); - } - - ParameterBinding binding = bindingFactory.apply(identifier); - - for (ParameterBinding existing : bindingsForOrigin) { - - if (existing.isCompatibleWith(binding)) { - return existing.getIdentifier(); - } - } - - BindingIdentifier syntheticIdentifier; - if (identifier.hasName() && methodArgument.hasName()) { - - int index = 0; - String newName = methodArgument.getName(); - while (existsBoundParameter(newName)) { - index++; - newName = methodArgument.getName() + "_" + index; - } - syntheticIdentifier = BindingIdentifier.of(newName); - } else { - syntheticIdentifier = BindingIdentifier.of(++syntheticParameterIndex); - } - - ParameterBinding newBinding = bindingFactory.apply(syntheticIdentifier); - registration.accept(newBinding); - bindingsForOrigin.add(newBinding); - return newBinding.getIdentifier(); - } - - private boolean existsBoundParameter(String key) { - return methodArgumentToLikeBindings.values().stream().flatMap(Collection::stream) - .anyMatch(it -> key.equals(it.getName())); - } - - private List getBindings(BindingIdentifier identifier) { - return methodArgumentToLikeBindings.computeIfAbsent(identifier, s -> new ArrayList<>()); - } - - public void register(ParameterBinding parameterBinding) { - registration.accept(parameterBinding); - } - } - - static { - - List keywords = new ArrayList<>(); - - for (ParameterBindingType type : ParameterBindingType.values()) { - if (type.getKeyword() != null) { - keywords.add(type.getKeyword()); - } - } - - StringBuilder builder = new StringBuilder(); - builder.append("("); - builder.append(StringUtils.collectionToDelimitedString(keywords, "|")); // keywords - builder.append(")?"); - builder.append("(?: )?"); // some whitespace - builder.append("\\(?"); // optional braces around parameters - builder.append("("); - builder.append("%?(" + POSITIONAL_OR_INDEXED_PARAMETER + ")%?"); // position parameter and parameter index - builder.append("|"); // or - - // named parameter and the parameter name - builder.append("%?(" + QueryUtils.COLON_NO_DOUBLE_COLON + QueryUtils.IDENTIFIER_GROUP + ")%?"); - - builder.append(")"); - builder.append("\\)?"); // optional braces around parameters - - PARAMETER_BINDING_PATTERN = Pattern.compile(builder.toString(), CASE_INSENSITIVE); - } - - /** - * Parses {@link ParameterBinding} instances from the given query and adds them to the registered bindings. Returns - * the cleaned up query. - */ - public String parseParameterBindingsOfQueryIntoBindingsAndReturnCleanedQuery(String query, List bindings, - Metadata queryMeta) { - - int greatestParameterIndex = tryFindGreatestParameterIndexIn(query); - boolean parametersShouldBeAccessedByIndex = greatestParameterIndex != -1; - - /* - * Prefer indexed access over named parameters if only SpEL Expression parameters are present. - */ - if (!parametersShouldBeAccessedByIndex && query.contains("?#{")) { - parametersShouldBeAccessedByIndex = true; - greatestParameterIndex = 0; - } - - ValueExpressionQueryRewriter.ParsedQuery parsedQuery = createSpelExtractor(query, - parametersShouldBeAccessedByIndex, - greatestParameterIndex); - - String resultingQuery = parsedQuery.getQueryString(); - Matcher matcher = PARAMETER_BINDING_PATTERN.matcher(resultingQuery); - - int expressionParameterIndex = parametersShouldBeAccessedByIndex ? greatestParameterIndex : 0; - int syntheticParameterIndex = expressionParameterIndex + parsedQuery.size(); - - ParameterBindings parameterBindings = new ParameterBindings(bindings, it -> checkAndRegister(it, bindings), - syntheticParameterIndex); - int currentIndex = 0; - - boolean usesJpaStyleParameters = false; - - while (matcher.find()) { - - if (parsedQuery.isQuoted(matcher.start())) { - continue; - } - - String parameterIndexString = matcher.group(INDEXED_PARAMETER_GROUP); - String parameterName = parameterIndexString != null ? null : matcher.group(NAMED_PARAMETER_GROUP); - Integer parameterIndex = getParameterIndex(parameterIndexString); - - String match = matcher.group(0); - if (JDBC_STYLE_PARAM.matcher(match).find()) { - queryMeta.usesJdbcStyleParameters = true; - } - - if (NUMBERED_STYLE_PARAM.matcher(match).find() || NAMED_STYLE_PARAM.matcher(match).find()) { - usesJpaStyleParameters = true; - } - - if (usesJpaStyleParameters && queryMeta.usesJdbcStyleParameters) { - throw new IllegalArgumentException("Mixing of ? parameters and other forms like ?1 is not supported"); - } - - String typeSource = matcher.group(COMPARISION_TYPE_GROUP); - Assert.isTrue(parameterIndexString != null || parameterName != null, - () -> String.format("We need either a name or an index; Offending query string: %s", query)); - ValueExpression expression = parsedQuery - .getParameter(parameterName == null ? parameterIndexString : parameterName); - String replacement = null; - - expressionParameterIndex++; - if ("".equals(parameterIndexString)) { - parameterIndex = expressionParameterIndex; - } - - BindingIdentifier queryParameter; - if (parameterIndex != null) { - queryParameter = BindingIdentifier.of(parameterIndex); - } else { - queryParameter = BindingIdentifier.of(parameterName); - } - ParameterOrigin origin = ObjectUtils.isEmpty(expression) - ? ParameterOrigin.ofParameter(parameterName, parameterIndex) - : ParameterOrigin.ofExpression(expression); - - BindingIdentifier targetBinding = queryParameter; - Function bindingFactory = switch (ParameterBindingType.of(typeSource)) { - case LIKE -> { - - Type likeType = LikeParameterBinding.getLikeTypeFrom(matcher.group(2)); - yield (identifier) -> new LikeParameterBinding(identifier, origin, likeType); - } - case IN -> (identifier) -> new InParameterBinding(identifier, origin); // fall-through we don't need a special parameter queryParameter for the given parameter. - default -> (identifier) -> new ParameterBinding(identifier, origin); - }; - - if (origin.isExpression()) { - parameterBindings.register(bindingFactory.apply(queryParameter)); - } else { - targetBinding = parameterBindings.register(queryParameter, origin, bindingFactory); - } - - replacement = targetBinding.hasName() ? ":" + targetBinding.getName() - : ((!usesJpaStyleParameters && queryMeta.usesJdbcStyleParameters) ? "?" - : "?" + targetBinding.getPosition()); - String result; - String substring = matcher.group(2); - - int index = resultingQuery.indexOf(substring, currentIndex); - if (index < 0) { - result = resultingQuery; - } else { - currentIndex = index + replacement.length(); - result = resultingQuery.substring(0, index) + replacement - + resultingQuery.substring(index + substring.length()); - } - - resultingQuery = result; - } - - return resultingQuery; - } - - private static ValueExpressionQueryRewriter.ParsedQuery createSpelExtractor(String queryWithSpel, - boolean parametersShouldBeAccessedByIndex, - int greatestParameterIndex) { - - /* - * If parameters need to be bound by index, we bind the synthetic expression parameters starting from position of the greatest discovered index parameter in order to - * not mix-up with the actual parameter indices. - */ - int expressionParameterIndex = parametersShouldBeAccessedByIndex ? greatestParameterIndex : 0; - - BiFunction indexToParameterName = parametersShouldBeAccessedByIndex - ? (index, expression) -> String.valueOf(index + expressionParameterIndex + 1) - : (index, expression) -> EXPRESSION_PARAMETER_PREFIX + (index + 1); - - String fixedPrefix = parametersShouldBeAccessedByIndex ? "?" : ":"; - - BiFunction parameterNameToReplacement = (prefix, name) -> fixedPrefix + name; - ValueExpressionQueryRewriter rewriter = ValueExpressionQueryRewriter.of(ValueExpressionParser.create(), - indexToParameterName, parameterNameToReplacement); - - return rewriter.parse(queryWithSpel); - } - - @Nullable - private static Integer getParameterIndex(@Nullable String parameterIndexString) { - - if (parameterIndexString == null || parameterIndexString.isEmpty()) { - return null; - } - return Integer.valueOf(parameterIndexString); - } - - private static int tryFindGreatestParameterIndexIn(String query) { - - Matcher parameterIndexMatcher = PARAMETER_BINDING_BY_INDEX.matcher(query); - - int greatestParameterIndex = -1; - while (parameterIndexMatcher.find()) { - - String parameterIndexString = parameterIndexMatcher.group(1); - Integer parameterIndex = getParameterIndex(parameterIndexString); - if (parameterIndex != null) { - greatestParameterIndex = Math.max(greatestParameterIndex, parameterIndex); - } - } - - return greatestParameterIndex; - } - - private static void checkAndRegister(ParameterBinding binding, List bindings) { - - bindings.stream() // - .filter(it -> it.bindsTo(binding)) // - .forEach(it -> Assert.isTrue(it.equals(binding), String.format(MESSAGE, it, binding))); - - if (!bindings.contains(binding)) { - bindings.add(binding); - } - } - - /** - * An enum for the different types of bindings. - * - * @author Thomas Darimont - * @author Oliver Gierke - */ - private enum ParameterBindingType { - - // Trailing whitespace is intentional to reflect that the keywords must be used with at least one whitespace - // character, while = does not. - LIKE("like "), IN("in "), AS_IS(null); - - private final @Nullable String keyword; - - ParameterBindingType(@Nullable String keyword) { - this.keyword = keyword; - } - - /** - * Returns the keyword that will trigger the binding type or {@literal null} if the type is not triggered by a - * keyword. - * - * @return the keyword - */ - @Nullable - public String getKeyword() { - return keyword; - } - - /** - * Return the appropriate {@link ParameterBindingType} for the given {@link String}. Returns {@literal #AS_IS} in - * case no other {@link ParameterBindingType} could be found. - */ - static ParameterBindingType of(String typeSource) { - - if (!StringUtils.hasText(typeSource)) { - return AS_IS; - } - - for (ParameterBindingType type : values()) { - if (type.name().equalsIgnoreCase(typeSource.trim())) { - return type; - } - } - - throw new IllegalArgumentException(String.format("Unsupported parameter binding type %s", typeSource)); - } - } -} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/TestJpaAotRepositoryContext.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/TestJpaAotRepositoryContext.java index aaf2e5218..216ed8ee1 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/TestJpaAotRepositoryContext.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/TestJpaAotRepositoryContext.java @@ -25,6 +25,8 @@ import java.util.Set; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.env.Environment; +import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.ClassPathResource; import org.springframework.core.test.tools.ClassFile; import org.springframework.data.jpa.domain.sample.Role; @@ -35,6 +37,8 @@ import org.springframework.data.repository.core.support.RepositoryComposition; import org.springframework.lang.Nullable; /** + * Test {@link AotRepositoryContext} implementation for JPA repositories. + * * @author Christoph Strobl */ public class TestJpaAotRepositoryContext implements AotRepositoryContext { @@ -56,6 +60,11 @@ public class TestJpaAotRepositoryContext implements AotRepositoryContext { return null; } + @Override + public Environment getEnvironment() { + return new StandardEnvironment(); + } + @Override public TypeIntrospector introspectType(String typeName) { return null; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java index 714abc2af..ba3f33f02 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/config/JpaRepositoryRegistrationAotProcessorUnitTests.java @@ -31,6 +31,8 @@ import org.springframework.aot.generate.InMemoryGeneratedFiles; import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.env.Environment; +import org.springframework.core.env.StandardEnvironment; import org.springframework.data.repository.config.AotRepositoryContext; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.javapoet.ClassName; @@ -116,6 +118,11 @@ class JpaRepositoryRegistrationAotProcessorUnitTests { return null; } + @Override + public Environment getEnvironment() { + return new StandardEnvironment(); + } + @Override public TypeIntrospector introspectType(String typeName) { return null; diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultEntityQueryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultEntityQueryUnitTests.java index a88c2912f..3077ded6b 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultEntityQueryUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/DefaultEntityQueryUnitTests.java @@ -28,7 +28,6 @@ import org.springframework.data.jpa.repository.query.ParameterBinding.InParamete import org.springframework.data.jpa.repository.query.ParameterBinding.LikeParameterBinding; import org.springframework.data.jpa.repository.query.ParameterBinding.MethodInvocationArgument; import org.springframework.data.jpa.repository.query.ParameterBinding.ParameterOrigin; -import org.springframework.data.jpa.repository.query.ParameterBindingParser.Metadata; import org.springframework.data.repository.query.parser.Part.Type; /**