Remove @Deprecated API.

Closes #3208
This commit is contained in:
Mark Paluch
2024-11-18 08:55:43 +01:00
parent b4c55baa2d
commit 7db177e3cd
65 changed files with 73 additions and 3041 deletions

View File

@@ -1,35 +0,0 @@
/*
* Copyright 2011-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.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to declare a constructor for instantiation.
*
* @author Jon Brisbin
* @author Mark Paluch
* @author Oliver Drotbohm
* @deprecated in favor of {@link PersistenceCreator} since 3.0, to be removed in 3.1
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.CONSTRUCTOR, ElementType.ANNOTATION_TYPE })
@PersistenceCreator
@Deprecated(forRemoval = true)
public @interface PersistenceConstructor {}

View File

@@ -160,7 +160,6 @@ public class Parameter<T, P extends PersistentProperty<P>> {
* Returns the expression to be used when looking up a source data structure to populate the actual parameter value.
*
* @return the expression to be used when looking up a source data structure.
* @deprecated since 3.3, use {@link #getValueExpression()} instead.
*/
@Nullable
public String getSpelExpression() {
@@ -194,17 +193,6 @@ public class Parameter<T, P extends PersistentProperty<P>> {
return getValueExpression();
}
/**
* Returns whether the constructor parameter is equipped with a SpEL expression.
*
* @return {@literal true}} if the parameter is equipped with a SpEL expression.
* @deprecated since 3.3, use {@link #hasValueExpression()} instead.
*/
@Deprecated(since = "3.3")
public boolean hasSpelExpression() {
return hasValueExpression();
}
/**
* Returns whether the constructor parameter is equipped with a value expression.
*

View File

@@ -43,19 +43,6 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
*/
String getName();
/**
* Returns the {@link PreferredConstructor} to be used to instantiate objects of this {@link PersistentEntity}.
*
* @return {@literal null} in case no suitable constructor for automatic construction can be found. This usually
* indicates that the instantiation of the object of that persistent entity is done through either a
* customer {@link org.springframework.data.mapping.model.EntityInstantiator} or handled by custom
* conversion mechanisms entirely.
* @deprecated since 3.0, use {@link #getInstanceCreatorMetadata()}.
*/
@Nullable
@Deprecated
PreferredConstructor<T, P> getPersistenceConstructor();
/**
* Returns the {@link InstanceCreatorMetadata} to be used to instantiate objects of this {@link PersistentEntity}.
*
@@ -68,20 +55,6 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
@Nullable
InstanceCreatorMetadata<P> getInstanceCreatorMetadata();
/**
* Returns whether the given {@link PersistentProperty} is referred to by a constructor argument of the
* {@link PersistentEntity}.
*
* @param property can be {@literal null}.
* @return true if the given {@link PersistentProperty} is referred to by a constructor argument or {@literal false}
* if not or {@literal null}.
* @deprecated since 3.0, use {@link #isCreatorArgument(PersistentProperty)} instead.
*/
@Deprecated
default boolean isConstructorArgument(PersistentProperty<?> property) {
return isCreatorArgument(property);
}
/**
* Returns whether the given {@link PersistentProperty} is referred to by a creator argument of the
* {@link PersistentEntity}.

View File

@@ -70,19 +70,6 @@ public interface PersistentPropertyPath<P extends PersistentProperty<P>> extends
*/
P getLeafProperty();
/**
* Returns the last property in the {@link PersistentPropertyPath}. So for {@code foo.bar} it will return the
* {@link PersistentProperty} for {@code bar}. For a simple {@code foo} it returns {@link PersistentProperty} for
* {@code foo}.
*
* @return will never be {@literal null}.
* @deprecated use {@link #getLeafProperty()} instead.
*/
@Deprecated(since = "3.1", forRemoval = true)
default P getRequiredLeafProperty() {
return getLeafProperty();
}
/**
* Returns the first property in the {@link PersistentPropertyPath}. So for {@code foo.bar} it will return the
* {@link PersistentProperty} for {@code foo}. For a simple {@code foo} it returns {@link PersistentProperty} for

View File

@@ -20,7 +20,6 @@ import java.util.Arrays;
import java.util.List;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.annotation.PersistenceCreator;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -36,7 +35,6 @@ import org.springframework.util.ReflectionUtils;
* @author Myeonghyeon Lee
* @author Xeno Amess
*/
@SuppressWarnings("deprecation")
public final class PreferredConstructor<T, P extends PersistentProperty<P>>
extends InstanceCreatorMetadataSupport<T, P> {
@@ -78,26 +76,15 @@ public final class PreferredConstructor<T, P extends PersistentProperty<P>>
}
/**
* Returns whether the constructor was explicitly selected (by {@link PersistenceConstructor}).
* Returns whether the constructor was explicitly selected (by {@link PersistenceCreator}).
*
* @return
* @return {@literal true} if the constructor was explicitly selected.
*/
public boolean isExplicitlyAnnotated() {
var annotations = MergedAnnotations.from(getExecutable());
return annotations.isPresent(PersistenceConstructor.class)
|| annotations.isPresent(PersistenceCreator.class);
}
/**
* @param property
* @return
* @deprecated since 3.0, use {@link #isCreatorParameter(PersistentProperty)} instead.
*/
@Deprecated
public boolean isConstructorParameter(PersistentProperty<?> property) {
return isCreatorParameter(property);
return annotations.isPresent(PersistenceCreator.class);
}
@Override

View File

@@ -130,13 +130,6 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
.anyMatch(it -> !(isCreatorArgument(it) || it.isTransient())));
}
@Nullable
@Override
@SuppressWarnings("unchecked")
public PreferredConstructor<T, P> getPersistenceConstructor() {
return creator instanceof PreferredConstructor ? (PreferredConstructor<T, P>) creator : null;
}
@Nullable
@Override
public InstanceCreatorMetadata<P> getInstanceCreatorMetadata() {

View File

@@ -32,6 +32,7 @@ import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.core.NativeDetector;
import org.springframework.data.mapping.FactoryMethod;
@@ -159,8 +160,7 @@ class ClassGeneratingEntityInstantiator implements EntityInstantiator {
* @return
*/
protected EntityInstantiator doCreateEntityInstantiator(PersistentEntity<?, ?> entity) {
return new EntityInstantiatorAdapter(
createObjectInstantiator(entity, entity.getInstanceCreatorMetadata()));
return new EntityInstantiatorAdapter(createObjectInstantiator(entity, entity.getInstanceCreatorMetadata()));
}
/**
@@ -239,7 +239,8 @@ class ClassGeneratingEntityInstantiator implements EntityInstantiator {
@Nullable InstanceCreatorMetadata<?> constructor) {
try {
return (ObjectInstantiator) this.generator.generateCustomInstantiatorClass(entity, constructor).newInstance();
Class<?> instantiatorClass = this.generator.generateCustomInstantiatorClass(entity, constructor);
return (ObjectInstantiator) BeanUtils.instantiateClass(instantiatorClass);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -482,8 +483,7 @@ class ClassGeneratingEntityInstantiator implements EntityInstantiator {
String entityTypeResourcePath = Type.getInternalName(entity.getType());
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, CREATE_METHOD_NAME,
"([" + BytecodeUtil.referenceName(Object.class) + ")" + BytecodeUtil.referenceName(Object.class),
null, null);
"([" + BytecodeUtil.referenceName(Object.class) + ")" + BytecodeUtil.referenceName(Object.class), null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, entityTypeResourcePath);
mv.visitInsn(DUP);

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2011-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.mapping.model;
import org.springframework.data.mapping.Parameter;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* {@link ParameterValueProvider} implementation that evaluates the {@link Parameter}s key against
* {@link SpelExpressionParser} and {@link EvaluationContext}.
*
* @author Oliver Gierke
* @deprecated since 3.3, use {@link CachingValueExpressionEvaluatorFactory} instead.
*/
@Deprecated(since = "3.3")
public class DefaultSpELExpressionEvaluator implements SpELExpressionEvaluator {
private final Object source;
private final SpELContext factory;
public DefaultSpELExpressionEvaluator(Object source, SpELContext factory) {
Assert.notNull(source, "Source must not be null");
Assert.notNull(factory, "SpELContext must not be null");
this.source = source;
this.factory = factory;
}
@Override
@Nullable
@SuppressWarnings("unchecked")
public <T> T evaluate(String expression) {
Expression parseExpression = factory.getParser().parseExpression(expression);
return (T) parseExpression.getValue(factory.getEvaluationContext(source));
}
}

View File

@@ -155,18 +155,6 @@ public class MappingInstantiationException extends RuntimeException {
return Optional.ofNullable(entityType);
}
/**
* The constructor used during the instantiation attempt.
*
* @return the constructor
* @deprecated since 3.0, use {@link #getEntityCreator()} instead.
*/
@Deprecated
public Optional<Constructor<?>> getConstructor() {
return getEntityCreator().filter(PreferredConstructor.class::isInstance).map(PreferredConstructor.class::cast)
.map(PreferredConstructor::getConstructor);
}
/**
* The entity creator used during the instantiation attempt.
*

View File

@@ -24,9 +24,7 @@ import org.springframework.lang.Nullable;
/**
* {@link ParameterValueProvider} based on a {@link PersistentEntity} to use a {@link PropertyValueProvider} to lookup
* the value of the property referenced by the given {@link Parameter}. Additionally a
* {@link DefaultSpELExpressionEvaluator} can be configured to get property value resolution trumped by a SpEL
* expression evaluation.
* the value of the property referenced by the given {@link Parameter}.
*
* @author Oliver Gierke
* @author Johannes Englmeier

View File

@@ -1,26 +0,0 @@
/*
* Copyright 2012-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.mapping.model;
/**
* SPI for components that can evaluate Spring EL expressions.
*
* @author Oliver Gierke
*/
@Deprecated(since = "3.3")
public interface SpELExpressionEvaluator extends ValueExpressionEvaluator {
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2012-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.mapping.model;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.PersistentProperty;
/**
* {@link ParameterValueProvider} that can be used to front a {@link ParameterValueProvider} delegate to prefer a SpEL
* expression evaluation over directly resolving the parameter value with the delegate.
*
* @author Oliver Gierke
* @author Mark Paluch
* @deprecated since 3.3, use {@link ValueExpressionParameterValueProvider} instead.
*/
@Deprecated(since = "3.3")
public class SpELExpressionParameterValueProvider<P extends PersistentProperty<P>>
extends ValueExpressionParameterValueProvider<P> implements ParameterValueProvider<P> {
public SpELExpressionParameterValueProvider(SpELExpressionEvaluator evaluator, ConversionService conversionService,
ParameterValueProvider<P> delegate) {
super(evaluator, conversionService, delegate);
}
}

View File

@@ -66,21 +66,6 @@ public class ValueExpressionParameterValueProvider<P extends PersistentProperty<
return object == null ? null : potentiallyConvertExpressionValue(object, parameter);
}
/**
* Hook to allow to massage the value resulting from the Spel expression evaluation. Default implementation will
* leverage the configured {@link ConversionService} to massage the value into the parameter type.
*
* @param object the value to massage, will never be {@literal null}.
* @param parameter the {@link Parameter} we create the value for
* @return the converted parameter value.
* @deprecated since 3.3, use {@link #potentiallyConvertExpressionValue(Object, Parameter)} instead.
*/
@Nullable
@Deprecated(since = "3.3")
protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, P> parameter) {
return conversionService.convert(object, parameter.getRawType());
}
/**
* Hook to allow to massage the value resulting from the Spel expression evaluation. Default implementation will
* leverage the configured {@link ConversionService} to massage the value into the parameter type.
@@ -92,6 +77,6 @@ public class ValueExpressionParameterValueProvider<P extends PersistentProperty<
*/
@Nullable
protected <T> T potentiallyConvertExpressionValue(Object object, Parameter<T, P> parameter) {
return potentiallyConvertSpelValue(object, parameter);
return conversionService.convert(object, parameter.getRawType());
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.projection;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -188,7 +189,7 @@ class SpelEvaluatingMethodInterceptor implements MethodInterceptor {
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(target);
result = 31 * result + ObjectUtils.nullSafeHashCode(args);
result = 31 * result + Arrays.hashCode(args);
return result;
}

View File

@@ -43,10 +43,8 @@ public class QPageRequest extends AbstractPageRequest {
*
* @param pageNumber zero-based page number, must not be negative.
* @param pageSize the size of the page to be returned, must be greater than 0.
* @deprecated since 2.1, use {@link #of(int, int)} instead.
*/
@Deprecated
public QPageRequest(int pageNumber, int pageSize) {
private QPageRequest(int pageNumber, int pageSize) {
this(pageNumber, pageSize, QSort.unsorted());
}
@@ -56,10 +54,8 @@ public class QPageRequest extends AbstractPageRequest {
* @param pageNumber zero-based page number, must not be negative.
* @param pageSize the size of the page to be returned, must be greater than 0.
* @param orderSpecifiers must not be {@literal null} or empty;
* @deprecated since 2.1, use {@link #of(int, int, OrderSpecifier...)} instead.
*/
@Deprecated
public QPageRequest(int pageNumber, int pageSize, OrderSpecifier<?>... orderSpecifiers) {
private QPageRequest(int pageNumber, int pageSize, OrderSpecifier<?>... orderSpecifiers) {
this(pageNumber, pageSize, new QSort(orderSpecifiers));
}
@@ -69,10 +65,8 @@ public class QPageRequest extends AbstractPageRequest {
* @param pageNumber zero-based page number, must not be negative.
* @param pageSize the size of the page to be returned, must be greater than 0.
* @param sort must not be {@literal null}.
* @deprecated since 2.1, use {@link #of(int, int, QSort)} instead.
*/
@Deprecated
public QPageRequest(int pageNumber, int pageSize, QSort sort) {
private QPageRequest(int pageNumber, int pageSize, QSort sort) {
super(pageNumber, pageSize);

View File

@@ -24,7 +24,7 @@ import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.support.QueryCreationListener;
import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.spel.EvaluationContextProvider;
/**
* Interface containing the configurable options for the Spring Data repository subsystem using CDI.
@@ -36,13 +36,13 @@ import org.springframework.data.repository.query.QueryMethodEvaluationContextPro
public interface CdiRepositoryConfiguration {
/**
* Return the {@link QueryMethodEvaluationContextProvider} to use. Can be {@link Optional#empty()} .
* Return the {@link EvaluationContextProvider} to use. Can be {@link Optional#empty()} .
*
* @return the optional {@link QueryMethodEvaluationContextProvider} base to use, can be {@link Optional#empty()},
* must not be {@literal null}.
* @return the optional {@link EvaluationContextProvider} base to use, can be {@link Optional#empty()}, must not be
* {@literal null}.
* @since 2.1
*/
default Optional<QueryMethodEvaluationContextProvider> getEvaluationContextProvider() {
default Optional<EvaluationContextProvider> getEvaluationContextProvider() {
return Optional.empty();
}

View File

@@ -76,23 +76,6 @@ public class AnnotationRepositoryConfigurationSource extends RepositoryConfigura
private final Function<AnnotationAttributes, Stream<TypeFilter>> typeFilterFunction;
private final boolean hasExplicitFilters;
/**
* Creates a new {@link AnnotationRepositoryConfigurationSource} from the given {@link AnnotationMetadata} and
* annotation.
*
* @param metadata must not be {@literal null}.
* @param annotation must not be {@literal null}.
* @param resourceLoader must not be {@literal null}.
* @param environment must not be {@literal null}.
* @param registry must not be {@literal null}.
* @deprecated since 2.2. Prefer to use overload taking a {@link BeanNameGenerator} additionally.
*/
@Deprecated(since = "2.2")
public AnnotationRepositoryConfigurationSource(AnnotationMetadata metadata, Class<? extends Annotation> annotation,
ResourceLoader resourceLoader, Environment environment, BeanDefinitionRegistry registry) {
this(metadata, annotation, resourceLoader, environment, registry, null);
}
/**
* Creates a new {@link AnnotationRepositoryConfigurationSource} from the given {@link AnnotationMetadata} and
* annotation.

View File

@@ -24,9 +24,6 @@ import org.reactivestreams.Publisher;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ValueExpressionDelegate;
import org.springframework.data.repository.util.ReactiveWrapperConverters;
import org.springframework.data.util.ReactiveWrappers;
@@ -61,29 +58,9 @@ public abstract class ReactiveRepositoryFactorySupport extends RepositoryFactory
}
}
/**
* Sets the {@link QueryMethodEvaluationContextProvider} to be used to evaluate SpEL expressions in manually defined
* queries.
*
* @param evaluationContextProvider can be {@literal null}, defaults to
* {@link ReactiveQueryMethodEvaluationContextProvider#DEFAULT}.
*/
@Override
public void setEvaluationContextProvider(QueryMethodEvaluationContextProvider evaluationContextProvider) {
super.setEvaluationContextProvider(
evaluationContextProvider == null ? ReactiveQueryMethodEvaluationContextProvider.DEFAULT
: evaluationContextProvider);
}
/**
* Returns the {@link QueryLookupStrategy} for the given {@link QueryLookupStrategy.Key} and
* {@link ValueExpressionDelegate}. Favor implementing this method over
* {@link #getQueryLookupStrategy(QueryLookupStrategy.Key, QueryMethodEvaluationContextProvider)} for extended
* {@link org.springframework.data.expression.ValueExpression} support.
* <p>
* This method delegates to
* {@link #getQueryLookupStrategy(QueryLookupStrategy.Key, QueryMethodEvaluationContextProvider)} unless overridden.
* </p>
* {@link ValueExpressionDelegate}.
*
* @param key can be {@literal null}.
* @param valueExpressionDelegate will never be {@literal null}.
@@ -93,8 +70,7 @@ public abstract class ReactiveRepositoryFactorySupport extends RepositoryFactory
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable QueryLookupStrategy.Key key,
ValueExpressionDelegate valueExpressionDelegate) {
return getQueryLookupStrategy(key,
new ReactiveExtensionAwareQueryMethodEvaluationContextProvider(getEvaluationContextProvider()));
return Optional.empty();
}
/**

View File

@@ -38,11 +38,9 @@ import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
import org.springframework.data.repository.query.ExtensionAwareQueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.data.util.Lazy;
@@ -183,18 +181,6 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
this.evaluationContextProvider = Optional.of(evaluationContextProvider);
}
/**
* Sets the {@link QueryMethodEvaluationContextProvider} to be used to evaluate SpEL expressions in manually defined
* queries.
*
* @param evaluationContextProvider must not be {@literal null}.
* @deprecated since 3.4, use {@link #setEvaluationContextProvider(EvaluationContextProvider)} instead.
*/
@Deprecated(since = "3.4", forRemoval = true)
public void setEvaluationContextProvider(QueryMethodEvaluationContextProvider evaluationContextProvider) {
setEvaluationContextProvider(evaluationContextProvider.getEvaluationContextProvider());
}
/**
* Register a {@link RepositoryFactoryCustomizer} to customize the {@link RepositoryFactorySupport repository factor}
* before creating the repository.
@@ -246,22 +232,7 @@ public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>,
*/
protected Optional<EvaluationContextProvider> createDefaultEvaluationContextProvider(
ListableBeanFactory beanFactory) {
return createDefaultQueryMethodEvaluationContextProvider(beanFactory)
.map(QueryMethodEvaluationContextProvider::getEvaluationContextProvider);
}
/**
* Create a default {@link QueryMethodEvaluationContextProvider} (or subclass) from {@link ListableBeanFactory}.
*
* @param beanFactory the bean factory to use.
* @return the default instance. May be {@link Optional#empty()}.
* @since 2.4
* @deprecated since 3.4, use {@link #createDefaultEvaluationContextProvider(ListableBeanFactory)} instead.
*/
@Deprecated(since = "3.4", forRemoval = true)
protected Optional<QueryMethodEvaluationContextProvider> createDefaultQueryMethodEvaluationContextProvider(
ListableBeanFactory beanFactory) {
return Optional.of(new ExtensionAwareQueryMethodEvaluationContextProvider(beanFactory));
return Optional.of(QueryMethodValueEvaluationContextAccessor.createEvaluationContextProvider(beanFactory));
}
@Override

View File

@@ -31,6 +31,7 @@ import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.beans.BeanUtils;
@@ -62,11 +63,9 @@ import org.springframework.data.repository.core.RepositoryMethodContextHolder;
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.DefaultRepositoryInvocationMulticaster;
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.NoOpRepositoryInvocationMulticaster;
import org.springframework.data.repository.query.ExtensionAwareQueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ValueExpressionDelegate;
@@ -211,20 +210,6 @@ public abstract class RepositoryFactorySupport
return this.environment;
}
/**
* Sets the {@link QueryMethodEvaluationContextProvider} to be used to evaluate SpEL expressions in manually defined
* queries.
*
* @param evaluationContextProvider can be {@literal null}, defaults to
* {@link QueryMethodEvaluationContextProvider#DEFAULT}.
* @deprecated since 3.4, use {@link #setEvaluationContextProvider(EvaluationContextProvider)} instead.
*/
@Deprecated(since = "3.4", forRemoval = true)
public void setEvaluationContextProvider(@Nullable QueryMethodEvaluationContextProvider evaluationContextProvider) {
setEvaluationContextProvider(evaluationContextProvider == null ? EvaluationContextProvider.DEFAULT
: evaluationContextProvider.getEvaluationContextProvider());
}
/**
* Sets the {@link EvaluationContextProvider} to be used to evaluate SpEL expressions in manually defined queries.
*
@@ -559,28 +544,7 @@ public abstract class RepositoryFactorySupport
protected abstract Class<?> getRepositoryBaseClass(RepositoryMetadata metadata);
/**
* Returns the {@link QueryLookupStrategy} for the given {@link Key} and {@link QueryMethodEvaluationContextProvider}.
*
* @param key can be {@literal null}.
* @param evaluationContextProvider will never be {@literal null}.
* @return the {@link QueryLookupStrategy} to use or {@literal null} if no queries should be looked up.
* @since 1.9
* @deprecated since 3.4, use {@link #getQueryLookupStrategy(Key, ValueExpressionDelegate)} instead to support
* {@link org.springframework.data.expression.ValueExpression} in query methods.
*/
@Deprecated(since = "3.4", forRemoval = true)
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
return Optional.empty();
}
/**
* Returns the {@link QueryLookupStrategy} for the given {@link Key} and {@link ValueExpressionDelegate}. Favor
* implementing this method over {@link #getQueryLookupStrategy(Key, QueryMethodEvaluationContextProvider)} for
* extended {@link org.springframework.data.expression.ValueExpression} support.
* <p>
* This method delegates to {@link #getQueryLookupStrategy(Key, QueryMethodEvaluationContextProvider)} unless
* overridden.
* Returns the {@link QueryLookupStrategy} for the given {@link Key} and {@link ValueExpressionDelegate}.
*
* @param key can be {@literal null}.
* @param valueExpressionDelegate will never be {@literal null}.
@@ -589,8 +553,7 @@ public abstract class RepositoryFactorySupport
*/
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
ValueExpressionDelegate valueExpressionDelegate) {
return getQueryLookupStrategy(key,
new ExtensionAwareQueryMethodEvaluationContextProvider(evaluationContextProvider));
return Optional.empty();
}
/**
@@ -625,21 +588,6 @@ public abstract class RepositoryFactorySupport
return instantiateClass(baseClass, constructorArguments);
}
/**
* Creates a repository of the repository base class defined in the given {@link RepositoryInformation} using
* reflection.
*
* @param baseClass
* @param constructorArguments
* @return
* @deprecated since 2.6 because it has a misleading name. Use {@link #instantiateClass(Class, Object...)} instead.
*/
@SuppressWarnings("unchecked")
@Deprecated
protected final <R> R getTargetRepositoryViaReflection(Class<?> baseClass, Object... constructorArguments) {
return instantiateClass(baseClass, constructorArguments);
}
/**
* Convenience method to instantiate a class using the given {@code constructorArguments} by looking up a matching
* constructor.

View File

@@ -169,18 +169,6 @@ public interface RepositoryFragment<T> {
private final @Nullable Class<T> interfaceClass;
private final T implementation;
/**
* Creates a new {@link ImplementedRepositoryFragment} for the given interface class and implementation.
*
* @param interfaceClass
* @param implementation
* @deprecated since 3.4 - use {@link ImplementedRepositoryFragment(Class, Object)} instead.
*/
@Deprecated(since = "3.4", forRemoval = true)
public ImplementedRepositoryFragment(Optional<Class<T>> interfaceClass, T implementation) {
this(interfaceClass.orElse(null), implementation);
}
/**
* Creates a new {@link ImplementedRepositoryFragment} for the given interface class and implementation.
*

View File

@@ -1,121 +0,0 @@
/*
* Copyright 2014-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.repository.query;
import java.util.List;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.data.expression.ValueEvaluationContext;
import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.data.spel.ExpressionDependencies;
import org.springframework.data.spel.spi.EvaluationContextExtension;
import org.springframework.expression.EvaluationContext;
import org.springframework.util.Assert;
/**
* An {@link QueryMethodEvaluationContextProvider} that assembles an {@link EvaluationContext} from a list of
* {@link EvaluationContextExtension} instances.
*
* @author Thomas Darimont
* @author Oliver Gierke
* @author Christoph Strobl
* @author Jens Schauder
* @author Johannes Englmeier
* @since 1.9
* @deprecated since 3.4 in favor of {@link QueryMethodValueEvaluationContextAccessor}.
*/
@SuppressWarnings("removal")
@Deprecated(since = "3.4", forRemoval = true)
public class ExtensionAwareQueryMethodEvaluationContextProvider implements QueryMethodEvaluationContextProvider {
private final QueryMethodValueEvaluationContextAccessor delegate;
/**
* Creates a new {@link ExtensionAwareQueryMethodEvaluationContextProvider}.
*
* @param evaluationContextProvider to lookup the {@link EvaluationContextExtension}s from, must not be
* {@literal null}.
*/
public ExtensionAwareQueryMethodEvaluationContextProvider(EvaluationContextProvider evaluationContextProvider) {
Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null");
this.delegate = new QueryMethodValueEvaluationContextAccessor(QueryMethodValueEvaluationContextAccessor.ENVIRONMENT,
evaluationContextProvider);
}
/**
* Creates a new {@link ExtensionAwareQueryMethodEvaluationContextProvider}.
*
* @param beanFactory the {@link ListableBeanFactory} to lookup the {@link EvaluationContextExtension}s from, must not
* be {@literal null}.
*/
public ExtensionAwareQueryMethodEvaluationContextProvider(ListableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
this.delegate = beanFactory instanceof ApplicationContext ctx ? new QueryMethodValueEvaluationContextAccessor(ctx)
: new QueryMethodValueEvaluationContextAccessor(QueryMethodValueEvaluationContextAccessor.ENVIRONMENT,
beanFactory);
}
/**
* Creates a new {@link ExtensionAwareQueryMethodEvaluationContextProvider} using the given
* {@link EvaluationContextExtension}s.
*
* @param extensions must not be {@literal null}.
*/
public ExtensionAwareQueryMethodEvaluationContextProvider(List<? extends EvaluationContextExtension> extensions) {
Assert.notNull(extensions, "EvaluationContextExtensions must not be null");
this.delegate = new QueryMethodValueEvaluationContextAccessor(QueryMethodValueEvaluationContextAccessor.ENVIRONMENT,
extensions);
}
ExtensionAwareQueryMethodEvaluationContextProvider(QueryMethodValueEvaluationContextAccessor delegate) {
this.delegate = delegate;
}
@Override
public EvaluationContextProvider getEvaluationContextProvider() {
return getDelegate().getEvaluationContextProvider();
}
public QueryMethodValueEvaluationContextAccessor getDelegate() {
return delegate;
}
@Override
public <T extends Parameters<?, ?>> EvaluationContext getEvaluationContext(T parameters, Object[] parameterValues) {
ValueEvaluationContext evaluationContext = delegate.create(parameters).getEvaluationContext(parameterValues);
return evaluationContext.getRequiredEvaluationContext();
}
@Override
public <T extends Parameters<?, ?>> EvaluationContext getEvaluationContext(T parameters, Object[] parameterValues,
ExpressionDependencies dependencies) {
ValueEvaluationContext evaluationContext = delegate.create(parameters).getEvaluationContext(parameterValues,
dependencies);
return evaluationContext.getRequiredEvaluationContext();
}
}

View File

@@ -71,17 +71,6 @@ public class Parameter {
TYPES = Collections.unmodifiableList(types);
}
/**
* Creates a new {@link Parameter} for the given {@link MethodParameter}.
*
* @param parameter must not be {@literal null}.
* @deprecated since 3.1, use {@link #Parameter(MethodParameter, TypeInformation)} instead.
*/
@Deprecated(since = "3.1", forRemoval = true)
protected Parameter(MethodParameter parameter) {
this(parameter, TypeInformation.of(Parameter.class));
}
/**
* Creates a new {@link Parameter} for the given {@link MethodParameter} and domain {@link TypeInformation}.
*

View File

@@ -63,20 +63,6 @@ public abstract class Parameters<S extends Parameters<S, T>, T extends Parameter
private int dynamicProjectionIndex;
/**
* Creates a new {@link Parameters} instance for the given {@link Method} and {@link Function} to create a
* {@link Parameter} instance from a {@link MethodParameter}.
*
* @param method must not be {@literal null}.
* @param parameterFactory must not be {@literal null}.
* @since 3.0.2
* @deprecated since 3.2.1, use {@link Parameters(ParametersSource, Function)} instead.
*/
@Deprecated(since = "3.2.1", forRemoval = true)
protected Parameters(Method method, Function<MethodParameter, T> parameterFactory) {
this(ParametersSource.of(method), parameterFactory);
}
/**
* Creates a new {@link Parameters} instance for the given {@link Method} and {@link Function} to create a
* {@link Parameter} instance from a {@link MethodParameter}.

View File

@@ -106,7 +106,7 @@ public class QueryMethod {
this.method = method;
this.unwrappedReturnType = potentiallyUnwrapReturnTypeFor(metadata, method);
this.metadata = metadata;
this.parameters = parametersFunction == null ? createParameters(method, metadata.getDomainTypeInformation())
this.parameters = parametersFunction == null ? createParameters(ParametersSource.of(metadata, method))
: parametersFunction.apply(ParametersSource.of(metadata, method));
this.domainClass = Lazy.of(() -> {
@@ -187,20 +187,6 @@ public class QueryMethod {
return TypeInformation.of(unwrappedReturnType).isCollectionLike();
}
/**
* Creates a {@link Parameters} instance.
*
* @param method must not be {@literal null}.
* @param domainType must not be {@literal null}.
* @return must not return {@literal null}.
* @deprecated since 3.2.1, use {@link #createParameters(ParametersSource)} instead.
* @since 3.0.2
*/
@Deprecated(since = "3.2.1", forRemoval = true)
protected Parameters<?, ?> createParameters(Method method, TypeInformation<?> domainType) {
return createParameters(ParametersSource.of(getMetadata(), method));
}
/**
* Creates a {@link Parameters} instance.
*

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2014-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.repository.query;
import java.util.Collections;
import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.data.spel.ExpressionDependencies;
import org.springframework.expression.EvaluationContext;
/**
* Provides a way to access a centrally defined potentially shared {@link EvaluationContext}.
*
* @author Thomas Darimont
* @author Oliver Gierke
* @author Christoph Strobl
* @since 1.9
* @deprecated since 3.4 in favor of {@link QueryMethodValueEvaluationContextAccessor}.
*/
@Deprecated(since = "3.4", forRemoval = true)
@SuppressWarnings("removal")
public interface QueryMethodEvaluationContextProvider {
QueryMethodEvaluationContextProvider DEFAULT = new ExtensionAwareQueryMethodEvaluationContextProvider(
Collections.emptyList());
/**
* Returns an {@link EvaluationContext} built using the given {@link Parameters} and parameter values.
*
* @param parameters the {@link Parameters} instance obtained from the query method the context is built for.
* @param parameterValues the values for the parameters.
* @return
*/
<T extends Parameters<?, ?>> EvaluationContext getEvaluationContext(T parameters, Object[] parameterValues);
/**
* Returns an {@link EvaluationContext} built using the given {@link Parameters} and parameter values.
*
* @param parameters the {@link Parameters} instance obtained from the query method the context is built for.
* @param parameterValues the values for the parameters.
* @return
*/
<T extends Parameters<?, ?>> EvaluationContext getEvaluationContext(T parameters, Object[] parameterValues,
ExpressionDependencies dependencies);
/**
* @return the underlying {@link EvaluationContextProvider}.
*/
EvaluationContextProvider getEvaluationContextProvider();
}

View File

@@ -115,7 +115,7 @@ public class QueryMethodValueEvaluationContextAccessor {
this.evaluationContextProvider = createEvaluationContextProvider(extensions);
}
private static EvaluationContextProvider createEvaluationContextProvider(ListableBeanFactory factory) {
public static EvaluationContextProvider createEvaluationContextProvider(ListableBeanFactory factory) {
return ReactiveWrappers.isAvailable(ReactiveWrappers.ReactiveLibrary.PROJECT_REACTOR)
? new ReactiveExtensionAwareEvaluationContextProvider(factory)

View File

@@ -1,99 +0,0 @@
/*
* Copyright 2014-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.repository.query;
import reactor.core.publisher.Mono;
import java.util.List;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.data.expression.ReactiveValueEvaluationContextProvider;
import org.springframework.data.expression.ValueEvaluationContext;
import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.data.spel.ExpressionDependencies;
import org.springframework.data.spel.spi.EvaluationContextExtension;
import org.springframework.data.spel.spi.ExtensionIdAware;
import org.springframework.expression.EvaluationContext;
/**
* An reactive {@link QueryMethodEvaluationContextProvider} that assembles an {@link EvaluationContext} from a list of
* {@link EvaluationContextExtension} and {@link org.springframework.data.spel.spi.ReactiveEvaluationContextExtension}.
* instances.
*
* @author Mark Paluch
* @since 2.4
* @deprecated since 3.4 in favor of {@link QueryMethodValueEvaluationContextAccessor}.
*/
@SuppressWarnings("removal")
@Deprecated(since = "3.4", forRemoval = true)
public class ReactiveExtensionAwareQueryMethodEvaluationContextProvider
extends ExtensionAwareQueryMethodEvaluationContextProvider
implements ReactiveQueryMethodEvaluationContextProvider {
/**
* Create a new {@link ReactiveExtensionAwareQueryMethodEvaluationContextProvider}.
*
* @param beanFactory the {@link ListableBeanFactory} to lookup the {@link EvaluationContextExtension}s from, must not
* be {@literal null}.
*/
public ReactiveExtensionAwareQueryMethodEvaluationContextProvider(ListableBeanFactory beanFactory) {
super(beanFactory);
}
/**
* Create a new {@link ReactiveExtensionAwareQueryMethodEvaluationContextProvider} using the given
* {@link EvaluationContextExtension}s and
* {@link org.springframework.data.spel.spi.ReactiveEvaluationContextExtension}s.
*
* @param extensions must not be {@literal null}.
*/
public ReactiveExtensionAwareQueryMethodEvaluationContextProvider(List<? extends ExtensionIdAware> extensions) {
super(new QueryMethodValueEvaluationContextAccessor(QueryMethodValueEvaluationContextAccessor.ENVIRONMENT,
extensions));
}
/**
* Creates a new {@link ReactiveExtensionAwareQueryMethodEvaluationContextProvider}.
*
* @param evaluationContextProvider to lookup the {@link EvaluationContextExtension}s from, must not be
* {@literal null}.
*/
public ReactiveExtensionAwareQueryMethodEvaluationContextProvider(
EvaluationContextProvider evaluationContextProvider) {
super(new QueryMethodValueEvaluationContextAccessor(QueryMethodValueEvaluationContextAccessor.ENVIRONMENT,
evaluationContextProvider));
}
@Override
public <T extends Parameters<?, ?>> Mono<EvaluationContext> getEvaluationContextLater(T parameters,
Object[] parameterValues) {
return createProvider(parameters).getEvaluationContextLater(parameterValues)
.map(ValueEvaluationContext::getRequiredEvaluationContext);
}
@Override
public <T extends Parameters<?, ?>> Mono<EvaluationContext> getEvaluationContextLater(T parameters,
Object[] parameterValues, ExpressionDependencies dependencies) {
return createProvider(parameters).getEvaluationContextLater(parameterValues, dependencies)
.map(ValueEvaluationContext::getRequiredEvaluationContext);
}
private ReactiveValueEvaluationContextProvider createProvider(Parameters<?, ?> parameters) {
return (ReactiveValueEvaluationContextProvider) getDelegate().create(parameters);
}
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright 2014-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.repository.query;
import reactor.core.publisher.Mono;
import java.util.Collections;
import org.springframework.data.spel.ExpressionDependencies;
import org.springframework.expression.EvaluationContext;
/**
* Provides a way to access a centrally defined potentially shared {@link EvaluationContext} by considering
* {@link org.springframework.data.spel.spi.ReactiveEvaluationContextExtension}.
*
* @author Mark Paluch
* @since 2.4
* @deprecated since 3.4 in favor of {@link QueryMethodValueEvaluationContextAccessor}.
*/
@Deprecated(since = "3.4", forRemoval = true)
public interface ReactiveQueryMethodEvaluationContextProvider extends QueryMethodEvaluationContextProvider {
ReactiveQueryMethodEvaluationContextProvider DEFAULT = new ReactiveExtensionAwareQueryMethodEvaluationContextProvider(
Collections.emptyList());
/**
* Return a {@link EvaluationContext} built using the given {@link Parameters} and parameter values.
*
* @param parameters the {@link Parameters} instance obtained from the query method the context is built for.
* @param parameterValues the values for the parameters.
* @return a mono that emits exactly one {@link EvaluationContext}.
*/
<T extends Parameters<?, ?>> Mono<EvaluationContext> getEvaluationContextLater(T parameters,
Object[] parameterValues);
/**
* Return a {@link EvaluationContext} built using the given {@link Parameters} and parameter values.
*
* @param parameters the {@link Parameters} instance obtained from the query method the context is built for.
* @param parameterValues the values for the parameters.
* @return a mono that emits exactly one {@link EvaluationContext}.
*/
<T extends Parameters<?, ?>> Mono<EvaluationContext> getEvaluationContextLater(T parameters, Object[] parameterValues,
ExpressionDependencies dependencies);
}

View File

@@ -1,93 +0,0 @@
/*
* Copyright 2018-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.repository.query;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.data.repository.query.SpelQueryContext.SpelExtractor;
import org.springframework.data.spel.ExpressionDependencies;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Evaluates SpEL expressions as extracted by the {@link SpelExtractor} based on parameter information from a method and
* parameter values from a method call.
*
* @author Jens Schauder
* @author Gerrit Meier
* @author Oliver Gierke
* @since 2.1
* @see SpelQueryContext#parse(String)
* @deprecated since 3.3, use {@link ValueExpressionQueryRewriter} instead.
*/
@SuppressWarnings("removal")
@Deprecated(since = "3.3", forRemoval = true)
public class SpelEvaluator {
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
private final Parameters<?, ?> parameters;
private final SpelExtractor extractor;
public SpelEvaluator(QueryMethodEvaluationContextProvider evaluationContextProvider, Parameters<?, ?> parameters,
SpelExtractor extractor) {
this.evaluationContextProvider = evaluationContextProvider;
this.parameters = parameters;
this.extractor = extractor;
}
/**
* Evaluate all the SpEL expressions in {@link SpelExtractor} based on values provided as an argument.
*
* @param values Parameter values. Must not be {@literal null}.
* @return a map from parameter name to evaluated value. Guaranteed to be not {@literal null}.
*/
public Map<String, Object> evaluate(Object[] values) {
Assert.notNull(values, "Values must not be null.");
Map<String, String> parameterMap = extractor.getParameterMap();
Map<String, Object> results = new LinkedHashMap<>(parameterMap.size());
parameterMap.forEach((parameter, expression) -> results.put(parameter, getSpElValue(expression, values)));
return results;
}
/**
* Returns the query string produced by the intermediate SpEL expression collection step.
*
* @return
*/
public String getQueryString() {
return extractor.getQueryString();
}
@Nullable
private Object getSpElValue(String expressionString, Object[] values) {
Expression expression = PARSER.parseExpression(expressionString);
EvaluationContext evaluationContext = evaluationContextProvider.getEvaluationContext(parameters, values,
ExpressionDependencies.discover(expression));
return expression.getValue(evaluationContext);
}
}

View File

@@ -1,357 +0,0 @@
/*
* Copyright 2018-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.repository.query;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Range.Bound;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* A {@literal SpelQueryContext} is able to find SpEL expressions in a query string and to replace them with bind
* variables.
* <p>
* Result o the parse process is a {@link SpelExtractor} which offers the transformed query string. Alternatively and
* preferred one may provide a {@link QueryMethodEvaluationContextProvider} via
* {@link #withEvaluationContextProvider(QueryMethodEvaluationContextProvider)} which will yield the more powerful
* {@link EvaluatingSpelQueryContext}.
* <p>
* Typical usage looks like
*
* <pre>
* <code>
SpelQueryContext.EvaluatingSpelQueryContext queryContext = SpelQueryContext
.of((counter, expression) -> String.format("__$synthetic$__%d", counter), String::concat)
.withEvaluationContextProvider(evaluationContextProvider);
SpelEvaluator spelEvaluator = queryContext.parse(query, queryMethod.getParameters());
spelEvaluator.evaluate(objects).forEach(parameterMap::addValue);
* </code>
* </pre>
*
* @author Jens Schauder
* @author Gerrit Meier
* @author Mark Paluch
* @since 2.1
* @deprecated since 3.3, use {@link ValueExpressionQueryRewriter} instead.
*/
@SuppressWarnings("removal")
@Deprecated(since = "3.3", forRemoval = true)
public class SpelQueryContext {
private static final String SPEL_PATTERN_STRING = "([:?])#\\{([^}]+)}";
private static final Pattern SPEL_PATTERN = Pattern.compile(SPEL_PATTERN_STRING);
/**
* A function from the index of a SpEL expression in a query and the actual SpEL expression to the parameter name to
* be used in place of the SpEL expression. A typical implementation is expected to look like
* <code>(index, spel) -> "__some_placeholder_" + index</code>
*/
private final BiFunction<Integer, String, String> parameterNameSource;
/**
* A function from a prefix used to demarcate a SpEL expression in a query and a parameter name as returned from
* {@link #parameterNameSource} to a {@literal String} to be used as a replacement of the SpEL in the query. The
* returned value should normally be interpretable as a bind parameter by the underlying persistence mechanism. A
* typical implementation is expected to look like <code>(prefix, name) -> prefix + name</code> or
* <code>(prefix, name) -> "{" + name + "}"</code>
*/
private final BiFunction<String, String, String> replacementSource;
private SpelQueryContext(BiFunction<Integer, String, String> parameterNameSource,
BiFunction<String, String, String> replacementSource) {
Assert.notNull(parameterNameSource, "Parameter name source must not be null");
Assert.notNull(replacementSource, "Replacement source must not be null");
this.parameterNameSource = parameterNameSource;
this.replacementSource = replacementSource;
}
public static SpelQueryContext of(BiFunction<Integer, String, String> parameterNameSource,
BiFunction<String, String, String> replacementSource) {
return new SpelQueryContext(parameterNameSource, replacementSource);
}
/**
* Parses the query for SpEL expressions using the pattern:
*
* <pre>
* &lt;prefix&gt;#{&lt;spel&gt;}
* </pre>
* <p>
* with prefix being the character ':' or '?'. Parsing honors quoted {@literal String}s enclosed in single or double
* quotation marks.
*
* @param query a query containing SpEL expressions in the format described above. Must not be {@literal null}.
* @return A {@link SpelExtractor} which makes the query with SpEL expressions replaced by bind parameters and a map
* from bind parameter to SpEL expression available. Guaranteed to be not {@literal null}.
*/
public SpelExtractor parse(String query) {
return new SpelExtractor(query);
}
/**
* Createsa {@link EvaluatingSpelQueryContext} from the current one and the given
* {@link QueryMethodEvaluationContextProvider}.
*
* @param provider must not be {@literal null}.
* @return
*/
public EvaluatingSpelQueryContext withEvaluationContextProvider(QueryMethodEvaluationContextProvider provider) {
Assert.notNull(provider, "QueryMethodEvaluationContextProvider must not be null");
return new EvaluatingSpelQueryContext(provider, parameterNameSource, replacementSource);
}
/**
* An extension of {@link SpelQueryContext} that can create {@link SpelEvaluator} instances as it also knows about a
* {@link QueryMethodEvaluationContextProvider}.
*
* @author Oliver Gierke
* @since 2.1
*/
public static class EvaluatingSpelQueryContext extends SpelQueryContext {
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
/**
* Creates a new {@link EvaluatingSpelQueryContext} for the given {@link QueryMethodEvaluationContextProvider},
* parameter name source and replacement source.
*
* @param evaluationContextProvider must not be {@literal null}.
* @param parameterNameSource must not be {@literal null}.
* @param replacementSource must not be {@literal null}.
*/
private EvaluatingSpelQueryContext(QueryMethodEvaluationContextProvider evaluationContextProvider,
BiFunction<Integer, String, String> parameterNameSource, BiFunction<String, String, String> replacementSource) {
super(parameterNameSource, replacementSource);
this.evaluationContextProvider = evaluationContextProvider;
}
/**
* Parses the query for SpEL expressions using the pattern:
*
* <pre>
* &lt;prefix&gt;#{&lt;spel&gt;}
* </pre>
* <p>
* with prefix being the character ':' or '?'. Parsing honors quoted {@literal String}s enclosed in single or double
* quotation marks.
*
* @param query a query containing SpEL expressions in the format described above. Must not be {@literal null}.
* @param parameters a {@link Parameters} instance describing query method parameters
* @return A {@link SpelEvaluator} which allows to evaluate the SpEL expressions. Will never be {@literal null}.
*/
public SpelEvaluator parse(String query, Parameters<?, ?> parameters) {
return new SpelEvaluator(evaluationContextProvider, parameters, parse(query));
}
}
/**
* Parses a query string, identifies the contained SpEL expressions, replaces them with bind parameters and offers a
* {@link Map} from those bind parameters to the SpEL expression.
* <p>
* The parser detects quoted parts of the query string and does not detect SpEL expressions inside such quoted parts
* of the query.
*
* @author Jens Schauder
* @author Oliver Gierke
* @author Mark Paluch
* @since 2.1
*/
public class SpelExtractor {
private static final int PREFIX_GROUP_INDEX = 1;
private static final int EXPRESSION_GROUP_INDEX = 2;
private final String query;
private final Map<String, String> expressions;
private final QuotationMap quotations;
/**
* Creates a SpelExtractor from a query String.
*
* @param query must not be {@literal null}.
*/
SpelExtractor(String query) {
Assert.notNull(query, "Query must not be null");
Map<String, String> expressions = new HashMap<>();
Matcher matcher = SPEL_PATTERN.matcher(query);
StringBuilder resultQuery = new StringBuilder();
QuotationMap quotedAreas = new QuotationMap(query);
int expressionCounter = 0;
int matchedUntil = 0;
while (matcher.find()) {
if (quotedAreas.isQuoted(matcher.start())) {
resultQuery.append(query, matchedUntil, matcher.end());
} else {
String spelExpression = matcher.group(EXPRESSION_GROUP_INDEX);
String prefix = matcher.group(PREFIX_GROUP_INDEX);
String parameterName = parameterNameSource.apply(expressionCounter, spelExpression);
String replacement = replacementSource.apply(prefix, parameterName);
resultQuery.append(query, matchedUntil, matcher.start());
resultQuery.append(replacement);
expressions.put(parameterName, spelExpression);
expressionCounter++;
}
matchedUntil = matcher.end();
}
resultQuery.append(query.substring(matchedUntil));
this.expressions = Collections.unmodifiableMap(expressions);
this.query = resultQuery.toString();
// recreate quotation map based on rewritten query.
this.quotations = new QuotationMap(this.query);
}
/**
* The query with all the SpEL expressions replaced with bind parameters.
*
* @return Guaranteed to be not {@literal null}.
*/
public String getQueryString() {
return query;
}
/**
* Return whether the {@link #getQueryString() query} at {@code index} is quoted.
*
* @param index
* @return {@literal true} if quoted; {@literal false} otherwise.
*/
public boolean isQuoted(int index) {
return quotations.isQuoted(index);
}
public String getParameter(String name) {
return expressions.get(name);
}
/**
* Returns the number of expressions in this extractor.
*
* @return the number of expressions in this extractor.
* @since 3.1.3
*/
public int size() {
return expressions.size();
}
/**
* A {@literal Map} from parameter name to SpEL expression.
*
* @return Guaranteed to be not {@literal null}.
*/
Map<String, String> getParameterMap() {
return expressions;
}
}
/**
* Value object to analyze a {@link String} to determine the parts of the {@link String} that are quoted and offers an
* API to query that information.
*
* @author Jens Schauder
* @author Oliver Gierke
* @since 2.1
*/
static class QuotationMap {
private static final Collection<Character> QUOTING_CHARACTERS = List.of('"', '\'');
private final List<Range<Integer>> quotedRanges = new ArrayList<>();
/**
* Creates a new {@link QuotationMap} for the query.
*
* @param query can be {@literal null}.
*/
public QuotationMap(@Nullable String query) {
if (query == null) {
return;
}
Character inQuotation = null;
int start = 0;
for (int i = 0; i < query.length(); i++) {
char currentChar = query.charAt(i);
if (QUOTING_CHARACTERS.contains(currentChar)) {
if (inQuotation == null) {
inQuotation = currentChar;
start = i;
} else if (currentChar == inQuotation) {
inQuotation = null;
quotedRanges.add(Range.from(Bound.inclusive(start)).to(Bound.inclusive(i)));
}
}
}
if (inQuotation != null) {
throw new IllegalArgumentException(
String.format("The string <%s> starts a quoted range at %d, but never ends it.", query, start));
}
}
/**
* Checks if a given index is within a quoted range.
*
* @param index to check if it is part of a quoted range.
* @return whether the query contains a quoted range at {@literal index}.
*/
public boolean isQuoted(int index) {
return quotedRanges.stream().anyMatch(r -> r.contains(index));
}
}
}

View File

@@ -205,7 +205,6 @@ public abstract class QueryExecutionConverters {
CustomCollections.registerConvertersIn(conversionService);
conversionService.addConverter(new NullableWrapperToCompletableFutureConverter());
conversionService.addConverter(new NullableWrapperToFutureConverter());
conversionService.addConverter(new IterableToStreamableConverter());
}
@@ -357,28 +356,6 @@ public abstract class QueryExecutionConverters {
protected abstract Object wrap(Object source);
}
/**
* A Spring {@link Converter} to support returning {@link Future} instances from repository methods.
*
* @author Oliver Gierke
*/
@Deprecated(since = "3.0", forRemoval = true)
@SuppressWarnings("removal")
private static class NullableWrapperToFutureConverter extends AbstractWrapperTypeConverter {
/**
* Creates a new {@link NullableWrapperToFutureConverter} using the given {@link ConversionService}.
*/
NullableWrapperToFutureConverter() {
super(new AsyncResult<>(null), List.of(ListenableFuture.class));
}
@Override
protected Object wrap(Object source) {
return new AsyncResult<>(source);
}
}
/**
* A Spring {@link Converter} to support returning {@link CompletableFuture} instances from repository methods.
*

View File

@@ -132,8 +132,9 @@ public abstract class ReactiveWrapperConverters {
/**
* Returns whether the given type is supported for wrapper type conversion.
* <p>
* NOTE: A reactive wrapper type might be supported in general by {@link ReactiveWrappers#supports(Class)} but not
* necessarily for conversion using this method.
* NOTE: A reactive wrapper type might be supported in general by
* {@link org.springframework.data.util.ReactiveWrappers#supports(Class)} but not necessarily for conversion using
* this method.
* </p>
*
* @param type must not be {@literal null}.

View File

@@ -1,165 +0,0 @@
/*
* Copyright 2016-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.repository.util;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.util.Assert;
/**
* Utility class to expose details about reactive wrapper types. This class exposes whether a reactive wrapper is
* supported in general and whether a particular type is suitable for no-value/single-value/multi-value usage.
* <p>
* Supported types are discovered by their availability on the class path. This class is typically used to determine
* multiplicity and whether a reactive wrapper type is acceptable for a specific operation.
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Oliver Gierke
* @author Gerrit Meier
* @author Hantsy Bai
* @since 2.0
* @see org.reactivestreams.Publisher
* @see io.reactivex.rxjava3.core.Single
* @see io.reactivex.rxjava3.core.Maybe
* @see io.reactivex.rxjava3.core.Observable
* @see io.reactivex.rxjava3.core.Completable
* @see io.reactivex.rxjava3.core.Flowable
* @see io.smallrye.mutiny.Multi
* @see io.smallrye.mutiny.Uni
* @see Mono
* @see Flux
* @deprecated since 3.0, use {@link org.springframework.data.util.ReactiveWrappers} instead as the utility was moved
* into the {@code org.springframework.data.util} package.
*/
@Deprecated(since = "3.0", forRemoval = true)
public abstract class ReactiveWrappers {
private ReactiveWrappers() {}
/**
* Enumeration of supported reactive libraries.
*
* @author Mark Paluch
* @deprecated use {@link org.springframework.data.util.ReactiveWrappers.ReactiveLibrary} instead.
*/
@Deprecated(since = "3.0", forRemoval = true)
public enum ReactiveLibrary {
PROJECT_REACTOR, RXJAVA3, KOTLIN_COROUTINES, MUTINY;
}
/**
* Returns {@literal true} if reactive support is available. More specifically, whether any of the libraries defined
* in {@link ReactiveLibrary} are on the class path.
*
* @return {@literal true} if reactive support is available.
*/
public static boolean isAvailable() {
return org.springframework.data.util.ReactiveWrappers.isAvailable();
}
/**
* Returns {@literal true} if the {@link ReactiveLibrary} is available.
*
* @param reactiveLibrary must not be {@literal null}.
* @return {@literal true} if the {@link ReactiveLibrary} is available.
*/
public static boolean isAvailable(ReactiveLibrary reactiveLibrary) {
Assert.notNull(reactiveLibrary, "Reactive library must not be null");
switch (reactiveLibrary) {
case PROJECT_REACTOR:
return org.springframework.data.util.ReactiveWrappers.PROJECT_REACTOR_PRESENT;
case RXJAVA3:
return org.springframework.data.util.ReactiveWrappers.RXJAVA3_PRESENT;
case KOTLIN_COROUTINES:
return org.springframework.data.util.ReactiveWrappers.PROJECT_REACTOR_PRESENT
&& org.springframework.data.util.ReactiveWrappers.KOTLIN_COROUTINES_PRESENT;
case MUTINY:
return org.springframework.data.util.ReactiveWrappers.MUTINY_PRESENT;
default:
throw new IllegalArgumentException(String.format("Reactive library %s not supported", reactiveLibrary));
}
}
/**
* Returns {@literal true} if the {@code type} is a supported reactive wrapper type.
*
* @param type must not be {@literal null}.
* @return {@literal true} if the {@code type} is a supported reactive wrapper type.
*/
public static boolean supports(Class<?> type) {
return org.springframework.data.util.ReactiveWrappers.supports(type);
}
/**
* Returns whether the given type uses any reactive wrapper type in its method signatures.
*
* @param type must not be {@literal null}.
* @return
*/
public static boolean usesReactiveType(Class<?> type) {
Assert.notNull(type, "Type must not be null");
return org.springframework.data.util.ReactiveWrappers.usesReactiveType(type);
}
/**
* Returns {@literal true} if {@code type} is a reactive wrapper type that contains no value.
*
* @param type must not be {@literal null}.
* @return {@literal true} if {@code type} is a reactive wrapper type that contains no value.
*/
public static boolean isNoValueType(Class<?> type) {
Assert.notNull(type, "Candidate type must not be null");
return org.springframework.data.util.ReactiveWrappers.isNoValueType(type);
}
/**
* Returns {@literal true} if {@code type} is a reactive wrapper type for a single value.
*
* @param type must not be {@literal null}.
* @return {@literal true} if {@code type} is a reactive wrapper type for a single value.
*/
public static boolean isSingleValueType(Class<?> type) {
Assert.notNull(type, "Candidate type must not be null");
return org.springframework.data.util.ReactiveWrappers.isSingleValueType(type);
}
/**
* Returns {@literal true} if {@code type} is a reactive wrapper type supporting multiple values ({@code 0..N}
* elements).
*
* @param type must not be {@literal null}.
* @return {@literal true} if {@code type} is a reactive wrapper type supporting multiple values ({@code 0..N}
* elements).
*/
public static boolean isMultiValueType(Class<?> type) {
Assert.notNull(type, "Candidate type must not be null");
return org.springframework.data.util.ReactiveWrappers.isMultiValueType(type);
}
}

View File

@@ -18,13 +18,13 @@ package org.springframework.data.spel.spi;
import java.util.Collections;
import java.util.Map;
import org.springframework.data.spel.ExtensionAwareEvaluationContextProvider;
import org.springframework.expression.EvaluationContext;
import org.springframework.lang.Nullable;
/**
* SPI to allow adding a set of properties and function definitions accessible via the root of an
* {@link EvaluationContext} provided by an
* {@link org.springframework.data.repository.query.ExtensionAwareQueryMethodEvaluationContextProvider}.
* {@link EvaluationContext} provided by an {@link ExtensionAwareEvaluationContextProvider}.
* <p>
* Extensions can be ordered by following Spring's {@link org.springframework.core.Ordered} conventions.
*

View File

@@ -22,7 +22,7 @@ import org.springframework.expression.EvaluationContext;
/**
* SPI to resolve a {@link EvaluationContextExtension} to make it accessible via the root of an
* {@link EvaluationContext} provided by a
* {@link org.springframework.data.repository.query.ExtensionAwareQueryMethodEvaluationContextProvider}.
* {@link org.springframework.data.spel.ReactiveExtensionAwareEvaluationContextProvider}.
* <p>
* Extensions can be ordered by following Spring's {@link org.springframework.core.Ordered} conventions.
*

View File

@@ -1,57 +0,0 @@
/*
* Copyright 2018-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.type;
import java.util.Set;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.data.type.classreading.MethodsMetadataReader;
/**
* Interface that defines abstract metadata of a specific class, in a form that does not require that class to be loaded
* yet.
*
* @author Mark Paluch
* @since 2.1
* @see MethodMetadata
* @see ClassMetadata
* @see MethodsMetadataReader#getMethodsMetadata()
* @deprecated since 3.0, use {@link MetadataReader} directly to obtain {@link AnnotationMetadata#getDeclaredMethods()
* declared methods} directly.
*/
@Deprecated
public interface MethodsMetadata extends ClassMetadata {
/**
* Return all methods.
*
* @return the methods declared in the class ordered as found in the class file. Order does not necessarily reflect
* the declaration order in the source file.
*/
Set<MethodMetadata> getMethods();
/**
* Return all methods matching method {@code name}.
*
* @param name name of the method, must not be {@literal null} or empty.
* @return the methods matching method {@code name } declared in the class ordered as found in the class file. Order
* does not necessarily reflect the declaration order in the source file.
*/
Set<MethodMetadata> getMethods(String name);
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2018-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.type.classreading;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.data.type.MethodsMetadata;
/**
* Extension to {@link MetadataReader} for accessing class metadata and method metadata as read by an ASM
* {@link org.springframework.asm.ClassReader}.
*
* @author Mark Paluch
* @since 2.1
* @deprecated since 3.0, use {@link MetadataReader} to obtain {@link AnnotationMetadata#getDeclaredMethods() declared
* methods} directly.
*/
@Deprecated
public interface MethodsMetadataReader extends MetadataReader {
/**
* @return the {@link MethodsMetadata} for methods in the class file.
*/
MethodsMetadata getMethodsMetadata();
}

View File

@@ -1,194 +0,0 @@
/*
* Copyright 2018-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.type.classreading;
import java.io.IOException;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.data.type.MethodsMetadata;
import org.springframework.lang.Nullable;
/**
* Extension of {@link SimpleMetadataReaderFactory} that reads {@link MethodsMetadata}, creating a new ASM
* {@link MethodsMetadataReader} for every request.
*
* @author Mark Paluch
* @since 2.1
* @deprecated since 3.0. Use {@link SimpleMetadataReaderFactory} directly.
*/
@Deprecated
public class MethodsMetadataReaderFactory extends SimpleMetadataReaderFactory {
/**
* Create a new {@link MethodsMetadataReaderFactory} for the default class loader.
*/
public MethodsMetadataReaderFactory() {}
/**
* Create a new {@link MethodsMetadataReaderFactory} for the given {@link ResourceLoader}.
*
* @param resourceLoader the Spring {@link ResourceLoader} to use (also determines the {@link ClassLoader} to use).
*/
public MethodsMetadataReaderFactory(@Nullable ResourceLoader resourceLoader) {
super(resourceLoader);
}
/**
* Create a new {@link MethodsMetadataReaderFactory} for the given {@link ClassLoader}.
*
* @param classLoader the class loader to use.
*/
public MethodsMetadataReaderFactory(@Nullable ClassLoader classLoader) {
super(classLoader);
}
@Override
public MethodsMetadataReader getMetadataReader(String className) throws IOException {
return new MetadataReaderWrapper(super.getMetadataReader(className));
}
@Override
public MethodsMetadataReader getMetadataReader(Resource resource) throws IOException {
return new MetadataReaderWrapper(super.getMetadataReader(resource));
}
private static class MetadataReaderWrapper implements MethodsMetadataReader {
private final MetadataReader delegate;
MetadataReaderWrapper(MetadataReader delegate) {
this.delegate = delegate;
}
@Override
public MethodsMetadata getMethodsMetadata() {
return new MethodsMetadataWrapper(getAnnotationMetadata(), getClassMetadata());
}
@Override
public Resource getResource() {
return delegate.getResource();
}
@Override
public ClassMetadata getClassMetadata() {
return delegate.getClassMetadata();
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
return delegate.getAnnotationMetadata();
}
}
private static class MethodsMetadataWrapper implements MethodsMetadata, ClassMetadata {
private final AnnotationMetadata annotationMetadata;
private final ClassMetadata classMetadata;
MethodsMetadataWrapper(AnnotationMetadata annotationMetadata, ClassMetadata classMetadata) {
this.annotationMetadata = annotationMetadata;
this.classMetadata = classMetadata;
}
@Override
public Set<MethodMetadata> getMethods() {
return annotationMetadata.getDeclaredMethods();
}
@Override
public Set<MethodMetadata> getMethods(String name) {
return annotationMetadata.getDeclaredMethods().stream().filter(it -> it.getMethodName().equals(name))
.collect(Collectors.toSet());
}
@Override
public String getClassName() {
return classMetadata.getClassName();
}
@Override
public boolean isInterface() {
return classMetadata.isInterface();
}
@Override
public boolean isAnnotation() {
return classMetadata.isAnnotation();
}
@Override
public boolean isAbstract() {
return classMetadata.isAbstract();
}
@Override
public boolean isConcrete() {
return classMetadata.isConcrete();
}
@Override
public boolean isFinal() {
return classMetadata.isFinal();
}
@Override
public boolean isIndependent() {
return classMetadata.isIndependent();
}
@Override
public boolean hasEnclosingClass() {
return classMetadata.hasEnclosingClass();
}
@Override
@Nullable
public String getEnclosingClassName() {
return classMetadata.getEnclosingClassName();
}
@Override
public boolean hasSuperClass() {
return classMetadata.hasSuperClass();
}
@Override
@Nullable
public String getSuperClassName() {
return classMetadata.getSuperClassName();
}
@Override
public String[] getInterfaceNames() {
return classMetadata.getInterfaceNames();
}
@Override
public String[] getMemberClassNames() {
return classMetadata.getMemberClassNames();
}
}
}

View File

@@ -1,6 +0,0 @@
/**
* Support classes for reading annotation and class-level metadata.
*/
@org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
package org.springframework.data.type.classreading;

View File

@@ -1,5 +0,0 @@
/**
* Core support package for type introspection.
*/
@org.springframework.lang.NonNullApi
package org.springframework.data.type;

View File

@@ -32,11 +32,9 @@ import org.springframework.util.ConcurrentLruCache;
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
* @deprecated since 3.0 to go package protected at some point. Refer to {@link TypeInformation} only.
*/
@Deprecated(since = "3.0", forRemoval = true)
@SuppressWarnings({ "rawtypes", "unchecked" })
public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
class ClassTypeInformation<S> extends TypeDiscoverer<S> {
private static final ConcurrentLruCache<ResolvableType, ClassTypeInformation<?>> cache = new ConcurrentLruCache<>(128,
ClassTypeInformation::new);
@@ -70,9 +68,7 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
* @param <S>
* @param type
* @return
* @deprecated since 3.0. Use {@link TypeInformation#of} instead.
*/
@Deprecated
public static <S> ClassTypeInformation<S> from(Class<S> type) {
return from(resolvableTypeCache.get(type));
}
@@ -84,31 +80,6 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
return (ClassTypeInformation<S>) cache.get(type);
}
/**
* Warning: Does not fully resolve generic arguments.
*
* @param method
* @return
* @deprecated since 3.0. Use {@link TypeInformation#fromReturnTypeOf(Method)} instead.
*/
@Deprecated
public static <S> TypeInformation<S> fromReturnTypeOf(Method method) {
return (TypeInformation<S>) TypeInformation.of(ResolvableType.forMethodReturnType(method));
}
/**
* @param method
* @param actualType can be {@literal null}.
* @return
*/
static TypeInformation<?> fromReturnTypeOf(Method method, @Nullable Class<?> actualType) {
var type = actualType == null ? ResolvableType.forMethodReturnType(method)
: ResolvableType.forMethodReturnType(method, actualType);
return TypeInformation.of(type);
}
@Override
public Class<S> getType() {
return type;

View File

@@ -290,20 +290,6 @@ public class PagedResourcesAssembler<T> implements RepresentationModelAssembler<
return Link.of(UriTemplate.of(builder.build().toString()), relation);
}
/**
* Return the {@link MethodParameter} to be used to potentially qualify the paging and sorting request parameters to.
* Default implementations returns {@literal null}, which means the parameters will not be qualified.
*
* @return
* @since 1.7
* @deprecated since 3.1, rather set up the instance with {@link #withParameter(MethodParameter)}.
*/
@Nullable
@Deprecated(since = "3.1", forRemoval = true)
protected MethodParameter getMethodParameter() {
return null;
}
/**
* Creates a new {@link PageMetadata} instance from the given {@link Page}.
*

View File

@@ -84,33 +84,6 @@ abstract class SpringDataAnnotationUtils {
return false;
}
/**
* Returns the value of the given specific property of the given annotation. If the value of that property is the
* properties default, we fall back to the value of the {@code value} attribute.
*
* @param annotation must not be {@literal null}.
* @param property must not be {@literal null} or empty.
* @return
* @deprecated since 3.0 as this method is no longer used within the Framework.
*/
@SuppressWarnings("unchecked")
@Deprecated
public static <T> T getSpecificPropertyOrDefaultFromValue(Annotation annotation, String property) {
Object propertyDefaultValue = AnnotationUtils.getDefaultValue(annotation, property);
Object propertyValue = AnnotationUtils.getValue(annotation, property);
Object result = ObjectUtils.nullSafeEquals(propertyDefaultValue, propertyValue) //
? AnnotationUtils.getValue(annotation) //
: propertyValue;
if (result == null) {
throw new IllegalStateException("Exepected to be able to look up an annotation property value but failed");
}
return (T) result;
}
/**
* Determine a qualifier value for a {@link MethodParameter}.
*

View File

@@ -26,7 +26,6 @@ import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.annotation.PersistenceCreator;
import org.springframework.data.mapping.PreferredConstructorDiscovererUnitTests.Outer.Inner;
import org.springframework.data.mapping.model.BasicPersistentEntity;
@@ -114,7 +113,7 @@ class PreferredConstructorDiscovererUnitTests<P extends PersistentProperty<P>> {
var constructor = PreferredConstructorDiscoverer.discover(entity);
assertThat(constructor).isNotNull();
var annotation = constructor.getConstructor().getAnnotation(PersistenceConstructor.class);
var annotation = constructor.getConstructor().getAnnotation(PersistenceCreator.class);
assertThat(annotation).isNotNull();
assertThat(constructor.getConstructor().isSynthetic()).isFalse();
}
@@ -184,7 +183,7 @@ class PreferredConstructorDiscovererUnitTests<P extends PersistentProperty<P>> {
}
static class SyntheticConstructor {
@PersistenceConstructor
@PersistenceCreator
private SyntheticConstructor(String x) {}
class InnerSynthetic {
@@ -225,7 +224,7 @@ class PreferredConstructorDiscovererUnitTests<P extends PersistentProperty<P>> {
public ClassWithMultipleConstructorsAndAnnotation(String value) {}
@PersistenceConstructor
@PersistenceCreator
public ClassWithMultipleConstructorsAndAnnotation(Long value) {}
}

View File

@@ -132,7 +132,6 @@ class ClassGeneratingEntityInstantiatorUnitTests<P extends PersistentProperty<P>
doReturn("FOO").when(provider).getParameterValue(any(Parameter.class));
Constructor constructor = Sample.class.getConstructor(Long.class, String.class);
List<Object> parameters = Arrays.asList("FOO", "FOO");
try {
@@ -142,12 +141,6 @@ class ClassGeneratingEntityInstantiatorUnitTests<P extends PersistentProperty<P>
} catch (MappingInstantiationException o_O) {
assertThat(o_O.getEntityCreator()
.map(it -> (PreferredConstructor) it)
.map(PreferredConstructor::getConstructor))
.isPresent()
.hasValue(constructor);
assertThat(o_O.getConstructorArguments()).isEqualTo(parameters);
assertThat(o_O.getEntityType()).hasValue(Sample.class);

View File

@@ -113,14 +113,12 @@ class ReflectionEntityInstantiatorUnitTests<P extends PersistentProperty<P>> {
}
@Test // DATACMNS-283
@SuppressWarnings({ "unchecked", "rawtypes" })
void capturesContextOnInstantiationException() throws Exception {
void capturesContextOnInstantiationException() {
PersistentEntity<Sample, P> entity = new BasicPersistentEntity<>(TypeInformation.of(Sample.class));
doReturn("FOO").when(provider).getParameterValue(any(Parameter.class));
Constructor constructor = Sample.class.getConstructor(Long.class, String.class);
List<Object> parameters = Arrays.asList("FOO", "FOO");
try {
@@ -130,12 +128,6 @@ class ReflectionEntityInstantiatorUnitTests<P extends PersistentProperty<P>> {
} catch (MappingInstantiationException o_O) {
assertThat(o_O.getEntityCreator()
.map(it -> (PreferredConstructor) it)
.map(PreferredConstructor::getConstructor))
.isPresent()
.hasValue(constructor);
assertThat(o_O.getConstructorArguments()).isEqualTo(parameters);
assertThat(o_O.getEntityType()).hasValue(Sample.class);

View File

@@ -1,126 +0,0 @@
/*
* Copyright 2012-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.mapping.model;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.Parameter;
import org.springframework.data.mapping.model.AbstractPersistentPropertyUnitTests.SamplePersistentProperty;
/**
* Unit tests for {@link SpELExpressionParameterValueProvider}.
*
* @author Oliver Gierke
* @author Mark Paluch
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class SpelExpressionParameterProviderUnitTests {
@Mock SpELExpressionEvaluator evaluator;
@Mock ParameterValueProvider<SamplePersistentProperty> delegate;
@Mock ConversionService conversionService;
private SpELExpressionParameterValueProvider<SamplePersistentProperty> provider;
private Parameter<Object, SamplePersistentProperty> parameter;
@BeforeEach
@SuppressWarnings("unchecked")
void setUp() {
provider = new SpELExpressionParameterValueProvider<>(evaluator, conversionService, delegate);
parameter = mock(Parameter.class);
when(parameter.hasValueExpression()).thenReturn(true);
when(parameter.getRawType()).thenReturn(Object.class);
}
@Test
@SuppressWarnings("unchecked")
void delegatesIfParameterDoesNotHaveASpELExpression() {
Parameter<Object, SamplePersistentProperty> parameter = mock(Parameter.class);
when(parameter.hasValueExpression()).thenReturn(false);
provider.getParameterValue(parameter);
verify(delegate, times(1)).getParameterValue(parameter);
verify(evaluator, times(0)).evaluate("expression");
}
@Test
void evaluatesSpELExpression() {
when(parameter.getRequiredValueExpression()).thenReturn("expression");
provider.getParameterValue(parameter);
verify(delegate, times(0)).getParameterValue(parameter);
verify(evaluator, times(1)).evaluate("#{expression}");
}
@Test
void handsSpELValueToConversionService() {
doReturn("source").when(parameter).getRequiredValueExpression();
doReturn("value").when(evaluator).evaluate(any());
provider.getParameterValue(parameter);
verify(delegate, times(0)).getParameterValue(parameter);
verify(conversionService, times(1)).convert("value", Object.class);
}
@Test
void doesNotConvertNullValue() {
doReturn("source").when(parameter).getRequiredValueExpression();
doReturn(null).when(evaluator).evaluate(any());
provider.getParameterValue(parameter);
verify(delegate, times(0)).getParameterValue(parameter);
verify(conversionService, times(0)).convert("value", Object.class);
}
@Test
void returnsMassagedObjectOnOverride() {
provider = new SpELExpressionParameterValueProvider<SamplePersistentProperty>(evaluator, conversionService,
delegate) {
@Override
@SuppressWarnings("unchecked")
protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, SamplePersistentProperty> parameter) {
return (T) "FOO";
}
};
doReturn("source").when(parameter).getRequiredValueExpression();
doReturn("value").when(evaluator).evaluate(any());
assertThat(provider.getParameterValue(parameter)).isEqualTo("FOO");
verify(delegate, times(0)).getParameterValue(parameter);
}
}

View File

@@ -50,8 +50,8 @@ import org.springframework.data.repository.core.support.QueryCreationListener;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.spel.EvaluationContextProvider;
/**
* Unit tests for {@link CdiRepositoryBean}.
@@ -182,7 +182,7 @@ class CdiRepositoryBeanUnitTests {
bean.applyConfiguration(repositoryFactory);
verify(repositoryFactory).setEvaluationContextProvider(QueryMethodEvaluationContextProvider.DEFAULT);
verify(repositoryFactory).setEvaluationContextProvider(EvaluationContextProvider.DEFAULT);
verify(repositoryFactory).setNamedQueries(PropertiesBasedNamedQueries.EMPTY);
verify(repositoryFactory).setRepositoryBaseClass(String.class);
verify(repositoryFactory).setQueryLookupStrategyKey(Key.CREATE);
@@ -217,8 +217,8 @@ class CdiRepositoryBeanUnitTests {
INSTANCE;
@Override
public Optional<QueryMethodEvaluationContextProvider> getEvaluationContextProvider() {
return Optional.of(QueryMethodEvaluationContextProvider.DEFAULT);
public Optional<EvaluationContextProvider> getEvaluationContextProvider() {
return Optional.of(EvaluationContextProvider.DEFAULT);
}
@Override

View File

@@ -45,7 +45,6 @@ import org.springframework.core.ResolvableType;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.data.mapping.Person;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.config.RepositoryConfigurationDelegate.LazyRepositoryInjectionPointResolver;
@@ -81,7 +80,7 @@ class RepositoryConfigurationDelegateUnitTests {
var context = new GenericApplicationContext();
RepositoryConfigurationSource configSource = new AnnotationRepositoryConfigurationSource(
new StandardAnnotationMetadata(TestConfig.class, true), EnableRepositories.class, context, environment,
AnnotationMetadata.introspect(TestConfig.class), EnableRepositories.class, context, environment,
context.getDefaultListableBeanFactory(), null);
var delegate = new RepositoryConfigurationDelegate(configSource, context, environment);
@@ -133,7 +132,7 @@ class RepositoryConfigurationDelegateUnitTests {
context.setApplicationStartup(startup);
RepositoryConfigurationSource configSource = new AnnotationRepositoryConfigurationSource(
new StandardAnnotationMetadata(TestConfig.class, true), EnableRepositories.class, context, environment,
AnnotationMetadata.introspect(TestConfig.class), EnableRepositories.class, context, environment,
context.getDefaultListableBeanFactory(), null);
var delegate = new RepositoryConfigurationDelegate(configSource, context, environment);

View File

@@ -29,8 +29,8 @@ import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ValueExpressionDelegate;
/**
* @author Mark Paluch
@@ -71,7 +71,7 @@ public class DummyReactiveRepositoryFactory extends ReactiveRepositoryFactorySup
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
ValueExpressionDelegate evaluationContextProvider) {
return Optional.of(strategy);
}

View File

@@ -36,8 +36,8 @@ import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ValueExpressionDelegate;
/**
* Dummy implementation for {@link RepositoryFactorySupport} that is equipped with mocks to simulate behavior for test
@@ -94,7 +94,7 @@ public class DummyRepositoryFactory extends RepositoryFactorySupport {
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
ValueExpressionDelegate valueExpressionDelegate) {
return Optional.of(strategy);
}

View File

@@ -23,6 +23,7 @@ import java.util.function.Supplier;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.core.metrics.StartupStep;
@@ -35,8 +36,8 @@ import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ValueExpressionDelegate;
/**
* Dummy implementation for {@link RepositoryFactorySupport} that is equipped with mocks to simulate behavior for test
@@ -93,7 +94,7 @@ public class ReactiveDummyRepositoryFactory extends ReactiveRepositoryFactorySup
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
ValueExpressionDelegate valueExpressionDelegate) {
return Optional.of(strategy);
}

View File

@@ -67,9 +67,9 @@ import org.springframework.data.repository.core.support.RepositoryMethodInvocati
import org.springframework.data.repository.query.QueryByExampleExecutor;
import org.springframework.data.repository.query.QueryCreationException;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ValueExpressionDelegate;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.data.repository.sample.User;
import org.springframework.lang.Nullable;
@@ -78,7 +78,6 @@ import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcesso
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.transaction.interceptor.TransactionalProxy;
import org.springframework.util.ClassUtils;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Unit tests for {@link RepositoryFactorySupport}.
@@ -523,7 +522,7 @@ class RepositoryFactorySupportUnitTests {
var factory = new DummyRepositoryFactory(backingRepo) {
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(QueryLookupStrategy.Key key,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
ValueExpressionDelegate valueExpressionDelegate) {
return Optional.of((method, metadata, factory, namedQueries) -> {
new PartTree(method.getName(), method.getReturnType());
return null;
@@ -653,11 +652,11 @@ class RepositoryFactorySupportUnitTests {
// DATACMNS-714
@Async
ListenableFuture<User> findOneByLastname(String lastname);
CompletableFuture<User> findOneByLastname(String lastname);
// DATACMNS-714
@Async
ListenableFuture<List<User>> readAllByLastname(String lastname);
CompletableFuture<List<User>> readAllByLastname(String lastname);
}
static class CustomRepositoryBaseClass {

View File

@@ -1,467 +0,0 @@
/*
* Copyright 2014-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.repository.query;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.spel.ExtensionAwareEvaluationContextProvider;
import org.springframework.data.spel.spi.EvaluationContextExtension;
import org.springframework.data.spel.spi.ExtensionIdAware;
import org.springframework.data.spel.spi.Function;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* Unit tests {@link ExtensionAwareQueryMethodEvaluationContextProvider}.
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author Jens Schauder
*/
class ExtensionAwareEvaluationContextProviderUnitTests {
Method method;
QueryMethodEvaluationContextProvider provider;
@BeforeEach
void setUp() throws Exception {
this.method = SampleRepo.class.getMethod("findByFirstname", String.class);
this.provider = new ExtensionAwareQueryMethodEvaluationContextProvider(Collections.emptyList());
}
@Test // DATACMNS-533
void usesPropertyDefinedByExtension() {
this.provider = new ExtensionAwareQueryMethodEvaluationContextProvider(
Collections.singletonList(new DummyExtension("_first", "first")));
assertThat(evaluateExpression("key")).isEqualTo("first");
}
@Test // DATACMNS-533
void secondExtensionOverridesFirstOne() {
List<EvaluationContextExtension> extensions = new ArrayList<>();
extensions.add(new DummyExtension("_first", "first"));
extensions.add(new DummyExtension("_second", "second"));
this.provider = new ExtensionAwareQueryMethodEvaluationContextProvider(extensions);
assertThat(evaluateExpression("key")).isEqualTo("second");
}
@Test // DATACMNS-533
void allowsDirectAccessToExtensionViaKey() {
List<EvaluationContextExtension> extensions = new ArrayList<>();
extensions.add(new DummyExtension("_first", "first"));
extensions.add(new DummyExtension("_second", "second"));
this.provider = new ExtensionAwareQueryMethodEvaluationContextProvider(extensions);
assertThat(evaluateExpression("_first.key")).isEqualTo("first");
}
@Test // DATACMNS-533
void exposesParametersAsVariables() {
assertThat(evaluateExpression("#firstname")).isEqualTo("parameterValue");
}
@Test // DATACMNS-533
void exposesMethodDefinedByExtension() {
this.provider = new ExtensionAwareQueryMethodEvaluationContextProvider(
Collections.singletonList(new DummyExtension("_first", "first")));
assertThat(evaluateExpression("aliasedMethod()")).isEqualTo("methodResult");
assertThat(evaluateExpression("extensionMethod()")).isEqualTo("methodResult");
assertThat(evaluateExpression("_first.extensionMethod()")).isEqualTo("methodResult");
assertThat(evaluateExpression("_first.aliasedMethod()")).isEqualTo("methodResult");
}
@Test // DATACMNS-533
void exposesPropertiesDefinedByExtension() {
this.provider = new ExtensionAwareQueryMethodEvaluationContextProvider(
Collections.singletonList(new DummyExtension("_first", "first")));
assertThat(evaluateExpression("DUMMY_KEY")).isEqualTo("dummy");
assertThat(evaluateExpression("_first.DUMMY_KEY")).isEqualTo("dummy");
}
@Test // DATACMNS-533
void exposesPageableParameter() throws Exception {
this.method = SampleRepo.class.getMethod("findByFirstname", String.class, Pageable.class);
var pageable = PageRequest.of(2, 3, Sort.by(Direction.DESC, "lastname"));
assertThat(evaluateExpression("#pageable.offset", new Object[] { "test", pageable })).isEqualTo(6L);
assertThat(evaluateExpression("#pageable.pageSize", new Object[] { "test", pageable })).isEqualTo(3);
assertThat(evaluateExpression("#pageable.sort.toString()", new Object[] { "test", pageable }))
.isEqualTo("lastname: DESC");
}
@Test // DATACMNS-533
void exposesSortParameter() throws Exception {
this.method = SampleRepo.class.getMethod("findByFirstname", String.class, Sort.class);
var sort = Sort.by(Direction.DESC, "lastname");
assertThat(evaluateExpression("#sort.toString()", new Object[] { "test", sort })).isEqualTo("lastname: DESC");
}
@Test // DATACMNS-533
void exposesSpecialParameterEvenIfItsNull() throws Exception {
this.method = SampleRepo.class.getMethod("findByFirstname", String.class, Sort.class);
assertThat(evaluateExpression("#sort?.toString()", new Object[] { "test", null })).isNull();
}
@Test // DATACMNS-533
void shouldBeAbleToAccessCustomRootObjectPropertiesAndFunctions() {
this.provider = new ExtensionAwareQueryMethodEvaluationContextProvider(Collections.singletonList( //
new DummyExtension("_first", "first") {
@Override
public CustomExtensionRootObject1 getRootObject() {
return new CustomExtensionRootObject1();
}
}));
assertThat(evaluateExpression("rootObjectInstanceField1")).isEqualTo("rootObjectInstanceF1");
assertThat(evaluateExpression("rootObjectInstanceMethod1()")).isEqualTo(true);
assertThat(evaluateExpression("getStringProperty()")).isEqualTo("stringProperty");
assertThat(evaluateExpression("stringProperty")).isEqualTo("stringProperty");
assertThat(evaluateExpression("_first.rootObjectInstanceField1")).isEqualTo("rootObjectInstanceF1");
assertThat(evaluateExpression("_first.rootObjectInstanceMethod1()")).isEqualTo(true);
assertThat(evaluateExpression("_first.getStringProperty()")).isEqualTo("stringProperty");
assertThat(evaluateExpression("_first.stringProperty")).isEqualTo("stringProperty");
}
@Test // DATACMNS-533
void shouldBeAbleToAccessCustomRootObjectPropertiesAndFunctionsInMultipleExtensions() {
this.provider = new ExtensionAwareQueryMethodEvaluationContextProvider(Arrays.asList( //
new DummyExtension("_first", "first") {
@Override
public CustomExtensionRootObject1 getRootObject() {
return new CustomExtensionRootObject1();
}
}, //
new DummyExtension("_second", "second") {
@Override
public CustomExtensionRootObject2 getRootObject() {
return new CustomExtensionRootObject2();
}
}));
assertThat(evaluateExpression("rootObjectInstanceField1")).isEqualTo("rootObjectInstanceF1");
assertThat(evaluateExpression("rootObjectInstanceMethod1()")).isEqualTo(true);
assertThat(evaluateExpression("rootObjectInstanceField2")).isEqualTo(42);
assertThat(evaluateExpression("rootObjectInstanceMethod2()")).isEqualTo("rootObjectInstanceMethod2");
assertThat(evaluateExpression("[0]")).isEqualTo("parameterValue");
}
@Test // DATACMNS-533
void shouldBeAbleToAccessCustomRootObjectPropertiesAndFunctionsFromDynamicTargetSource() {
final var counter = new AtomicInteger();
this.provider = new ExtensionAwareQueryMethodEvaluationContextProvider(Collections.singletonList( //
new DummyExtension("_first", "first") {
@Override
public CustomExtensionRootObject1 getRootObject() {
counter.incrementAndGet();
return new CustomExtensionRootObject1();
}
}) //
);
// inc counter / property access
assertThat(evaluateExpression("rootObjectInstanceField1")).isEqualTo("rootObjectInstanceF1");
// inc counter / function invocation
assertThat(evaluateExpression("rootObjectInstanceMethod1()")).isEqualTo(true);
assertThat(counter.get()).isEqualTo(2);
}
@Test // DATACMNS-1026
void overloadedMethodsGetResolved() throws Exception {
provider = createContextProviderWithOverloads();
// from the root object
assertThat(evaluateExpression("method()")).isEqualTo("zero");
assertThat(evaluateExpression("method(23)")).isEqualTo("single-int");
assertThat(evaluateExpression("method('hello')")).isEqualTo("single-string");
assertThat(evaluateExpression("method('one', 'two')")).isEqualTo("two");
// from the extension
assertThat(evaluateExpression("method(1, 2)")).isEqualTo("two-ints");
assertThat(evaluateExpression("method(1, 'two')")).isEqualTo("int-and-string");
}
@Test // DATACMNS-1026
void methodFromRootObjectOverwritesMethodFromExtension() throws Exception {
provider = createContextProviderWithOverloads();
assertThat(evaluateExpression("ambiguous()")).isEqualTo("from-root");
}
@Test // DATACMNS-1026
void aliasedMethodOverwritesMethodFromRootObject() throws Exception {
provider = createContextProviderWithOverloads();
assertThat(evaluateExpression("aliasedMethod()")).isEqualTo("methodResult");
}
@Test // DATACMNS-1026
void exactMatchIsPreferred() throws Exception {
provider = createContextProviderWithOverloads();
assertThat(evaluateExpression("ambiguousOverloaded('aString')")).isEqualTo("string");
}
@Test // DATACMNS-1026
void throwsExceptionWhenStillAmbiguous() throws Exception {
provider = createContextProviderWithOverloads();
assertThatIllegalStateException() //
.isThrownBy(() -> evaluateExpression("ambiguousOverloaded(23)")) //
.withMessageContaining("ambiguousOverloaded") //
.withMessageContaining("(java.lang.Integer)");
}
@Test // DATACMNS-1518
void invokesMethodWithVarArgs() {
provider = createContextProviderWithOverloads();
assertThat(evaluateExpression("methodWithVarArgs('one', 'two')")).isEqualTo("varargs");
}
@Test // DATACMNS-1534
void contextProviderShouldLazilyLookUpExtensions() {
var beanFactory = Mockito.mock(ListableBeanFactory.class);
var contextProvider = new ExtensionAwareEvaluationContextProvider(beanFactory);
verify(beanFactory, never()).getBeansOfType(eq(EvaluationContextExtension.class), anyBoolean(), anyBoolean());
contextProvider.getEvaluationContext(null);
verify(beanFactory).getBeansOfType(eq(ExtensionIdAware.class), anyBoolean(), anyBoolean());
}
@Test // DATACMNS-1534
void contextProviderShouldLookupExtensionsOnlyOnce() {
var beanFactory = Mockito.mock(ListableBeanFactory.class);
var contextProvider = new ExtensionAwareEvaluationContextProvider(beanFactory);
contextProvider.getEvaluationContext(null);
contextProvider.getEvaluationContext(null);
verify(beanFactory).getBeansOfType(eq(ExtensionIdAware.class), anyBoolean(), anyBoolean());
}
private static ExtensionAwareQueryMethodEvaluationContextProvider createContextProviderWithOverloads() {
return new ExtensionAwareQueryMethodEvaluationContextProvider(Collections.singletonList( //
new DummyExtension("_first", "first") {
@Override
public Object getRootObject() {
return new RootWithOverloads();
}
}));
}
public static class DummyExtension implements org.springframework.data.spel.spi.EvaluationContextExtension {
public static String DUMMY_KEY = "dummy";
private final String key, value;
public DummyExtension(String key, String value) {
this.key = key;
this.value = value;
}
@Override
public String getExtensionId() {
return key;
}
@Override
public Map<String, Object> getProperties() {
Map<String, Object> properties = new HashMap<>();
properties.put("key", value);
return properties;
}
@Override
public Map<String, Function> getFunctions() {
Map<String, Function> functions = new HashMap<>();
try {
functions.put("aliasedMethod", new Function(getClass().getMethod("extensionMethod")));
return functions;
} catch (Exception o_O) {
throw new RuntimeException(o_O);
}
}
public static String extensionMethod() {
return "methodResult";
}
public static String method(int i1, int i2) {
return "two-ints";
}
public static String method(int i, String s) {
return "int-and-string";
}
public static String ambiguous() {
return "from-extension-type";
}
public static String ambiguousToo() {
return "from-extension-type";
}
}
private Object evaluateExpression(String expression) {
return evaluateExpression(expression, new Object[] { "parameterValue" });
}
private Object evaluateExpression(String expression, Object[] args) {
var parameters = new DefaultParameters(ParametersSource.of(method));
var evaluationContext = provider.getEvaluationContext(parameters, args);
return new SpelExpressionParser().parseExpression(expression).getValue(evaluationContext);
}
interface SampleRepo {
List<Object> findByFirstname(@Param("firstname") String firstname);
List<Object> findByFirstname(@Param("firstname") String firstname, Pageable pageable);
List<Object> findByFirstname(@Param("firstname") String firstname, Sort sort);
}
public static class CustomExtensionRootObject1 {
public String rootObjectInstanceField1 = "rootObjectInstanceF1";
public boolean rootObjectInstanceMethod1() {
return true;
}
public String getStringProperty() {
return "stringProperty";
}
}
public static class CustomExtensionRootObject2 {
public Integer rootObjectInstanceField2 = 42;
public String rootObjectInstanceMethod2() {
return "rootObjectInstanceMethod2";
}
}
public static class RootWithOverloads {
public String method() {
return "zero";
}
public String method(String s) {
return "single-string";
}
public String method(int i) {
return "single-int";
}
public String method(String s1, String s2) {
return "two";
}
public String ambiguous() {
return "from-root";
}
public String aliasedMethod() {
return "from-root";
}
public String ambiguousOverloaded(String s) {
return "string";
}
public String ambiguousOverloaded(Object o) {
return "object";
}
public String ambiguousOverloaded(Serializable o) {
return "serializable";
}
public String methodWithVarArgs(String... args) {
return "varargs";
}
}
}

View File

@@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;
import org.springframework.core.MethodParameter;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.ParametersUnitTests.User;
import org.springframework.data.util.TypeInformation;
@@ -42,7 +43,7 @@ class ParameterUnitTests {
@Test // DATAJPA-1185
void classParameterWithSameTypeParameterAsReturnedListIsDynamicProjectionParameter() throws Exception {
var parameter = new Parameter(getMethodParameter("dynamicProjectionWithList"));
var parameter = new Parameter(getMethodParameter("dynamicProjectionWithList"), TypeInformation.MAP);
assertThat(parameter.isDynamicProjectionParameter()).isTrue();
}
@@ -50,7 +51,7 @@ class ParameterUnitTests {
@Test // DATAJPA-1185
void classParameterWithSameTypeParameterAsReturnedStreamIsDynamicProjectionParameter() throws Exception {
var parameter = new Parameter(getMethodParameter("dynamicProjectionWithStream"));
var parameter = new Parameter(getMethodParameter("dynamicProjectionWithStream"), TypeInformation.MAP);
assertThat(parameter.isDynamicProjectionParameter()).isTrue();
}
@@ -58,7 +59,7 @@ class ParameterUnitTests {
@Test
void classParameterWithSameTypeParameterAsReturnedOptionalIsDynamicProjectionParameter() throws Exception {
var parameter = new Parameter(getMethodParameter("dynamicProjectionWithOptional"));
var parameter = new Parameter(getMethodParameter("dynamicProjectionWithOptional"), TypeInformation.MAP);
assertThat(parameter.isDynamicProjectionParameter()).isTrue();
}
@@ -80,7 +81,7 @@ class ParameterUnitTests {
@Test // #2770
void doesNotConsiderAtParamAnnotatedClassParameterDynamicProjectionOne() throws Exception {
var parameter = new Parameter(getMethodParameter("atParamOnClass"));
var parameter = new Parameter(getMethodParameter("atParamOnClass"), TypeInformation.OBJECT);
assertThat(parameter.isDynamicProjectionParameter()).isFalse();
}

View File

@@ -1,132 +0,0 @@
/*
* Copyright 2018-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.repository.query;
import static org.assertj.core.api.Assertions.*;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Test;
import org.springframework.data.repository.query.SpelQueryContext.QuotationMap;
/**
* Unit tests for {@link QuotationMap}.
*
* @author Jens Schauder
*/
class QuotationMapUnitTests {
SoftAssertions softly = new SoftAssertions();
@Test // DATAJPA-1235
void emptyStringDoesNotContainQuotes() {
isNotQuoted("", "empty String", -1, 0, 1);
}
@Test // DATAJPA-1235
void nullStringDoesNotContainQuotes() {
isNotQuoted(null, "null String", -1, 0, 1);
}
@Test // DATAJPA-1235
void simpleStringDoesNotContainQuotes() {
var query = "something";
isNotQuoted(query, "simple String", -1, 0, query.length() - 1, query.length(), query.length() + 1);
}
@Test // DATAJPA-1235
void fullySingleQuotedStringDoesContainQuotes() {
var query = "'something'";
isNotQuoted(query, "quoted String", -1, query.length());
isQuoted(query, "quoted String", 0, 1, 5, query.length() - 1);
}
@Test // DATAJPA-1235
void fullyDoubleQuotedStringDoesContainQuotes() {
var query = "\"something\"";
isNotQuoted(query, "double quoted String", -1, query.length());
isQuoted(query, "double quoted String", 0, 1, 5, query.length() - 1);
}
@Test // DATAJPA-1235
void stringWithEmptyQuotes() {
var query = "abc''def";
isNotQuoted(query, "zero length quote", -1, 0, 1, 2, 5, 6, 7);
isQuoted(query, "zero length quote", 3, 4);
}
@Test // DATAJPA-1235
void doubleInSingleQuotes() {
var query = "abc'\"'def";
isNotQuoted(query, "double inside single quote", -1, 0, 1, 2, 6, 7, 8);
isQuoted(query, "double inside single quote", 3, 4, 5);
}
@Test // DATAJPA-1235
void singleQuotesInDoubleQuotes() {
var query = "abc\"'\"def";
isNotQuoted(query, "single inside double quote", -1, 0, 1, 2, 6, 7, 8);
isQuoted(query, "single inside double quote", 3, 4, 5);
}
@Test // DATAJPA-1235
void escapedQuotes() {
var query = "a'b''cd''e'f";
isNotQuoted(query, "escaped quote", -1, 0, 11, 12);
isQuoted(query, "escaped quote", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}
@Test // DATAJPA-1235
void openEndedQuoteThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new QuotationMap("a'b"));
}
private static void isNotQuoted(String query, Object label, int... indexes) {
var quotationMap = new QuotationMap(query);
for (var index : indexes) {
assertThat(quotationMap.isQuoted(index))
.describedAs(String.format("(%s) %s does not contain a quote at %s", label, query, index)) //
.isFalse();
}
}
private static void isQuoted(String query, Object label, int... indexes) {
var quotationMap = new QuotationMap(query);
for (var index : indexes) {
assertThat(quotationMap.isQuoted(index))
.describedAs(String.format("(%s) %s does contain a quote at %s", label, query, index)) //
.isTrue();
}
}
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright 2023-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.repository.query;
import static org.assertj.core.api.Assertions.*;
import java.lang.reflect.Method;
import org.junit.jupiter.api.Test;
import org.springframework.data.repository.query.SpelQueryContext.SpelExtractor;
/**
* Unit tests for {@link SpelEvaluator}.
*
* @author Mark Paluch
*/
class SpelEvaluatorUnitTests {
final SpelQueryContext context = SpelQueryContext.of((counter, s) -> String.format("__$synthetic$__%d", counter + 1),
String::concat);
@Test // GH-2904
void shouldEvaluateExpression() throws Exception {
SpelExtractor extractor = context.parse("SELECT :#{#value}");
Method method = MyRepository.class.getDeclaredMethod("simpleExpression", String.class);
SpelEvaluator evaluator = new SpelEvaluator(QueryMethodEvaluationContextProvider.DEFAULT,
new DefaultParameters(ParametersSource.of(method)), extractor);
assertThat(evaluator.getQueryString()).isEqualTo("SELECT :__$synthetic$__1");
assertThat(evaluator.evaluate(new Object[] { "hello" })).containsEntry("__$synthetic$__1", "hello");
}
@Test // GH-2904
void shouldAllowNullValues() throws Exception {
SpelExtractor extractor = context.parse("SELECT :#{#value}");
Method method = MyRepository.class.getDeclaredMethod("simpleExpression", String.class);
SpelEvaluator evaluator = new SpelEvaluator(QueryMethodEvaluationContextProvider.DEFAULT,
new DefaultParameters(ParametersSource.of(method)), extractor);
assertThat(evaluator.getQueryString()).isEqualTo("SELECT :__$synthetic$__1");
assertThat(evaluator.evaluate(new Object[] { null })).containsEntry("__$synthetic$__1", null);
}
interface MyRepository {
void simpleExpression(String value);
}
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright 2018-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.repository.query;
import static org.assertj.core.api.Assertions.*;
import java.util.Arrays;
import java.util.Map;
import java.util.function.BiFunction;
import org.assertj.core.api.SoftAssertions;
import org.assertj.core.groups.Tuple;
import org.junit.jupiter.api.Test;
import org.springframework.data.repository.query.SpelQueryContext.SpelExtractor;
/**
* Unit tests for {@link SpelExtractor}.
*
* @author Jens Schauder
* @author Oliver Gierke
*/
class SpelExtractorUnitTests {
static final BiFunction<Integer, String, String> PARAMETER_NAME_SOURCE = (index, spel) -> "EPP" + index;
static final BiFunction<String, String, String> REPLACEMENT_SOURCE = (prefix, name) -> prefix + name;
final SoftAssertions softly = new SoftAssertions();
@Test // DATACMNS-1258
void nullQueryThrowsException() {
var context = SpelQueryContext.of(PARAMETER_NAME_SOURCE, REPLACEMENT_SOURCE);
assertThatIllegalArgumentException().isThrownBy(() -> context.parse(null));
}
@Test // DATACMNS-1258
void emptyStringGetsParsedCorrectly() {
var context = SpelQueryContext.of(PARAMETER_NAME_SOURCE, REPLACEMENT_SOURCE);
var extractor = context.parse("");
softly.assertThat(extractor.getQueryString()).isEqualTo("");
softly.assertThat(extractor.getParameterMap()).isEmpty();
softly.assertAll();
}
@Test // DATACMNS-1258
void findsAndReplacesExpressions() {
var context = SpelQueryContext.of(PARAMETER_NAME_SOURCE, REPLACEMENT_SOURCE);
var extractor = context.parse(":#{one} ?#{two}");
softly.assertThat(extractor.getQueryString()).isEqualTo(":EPP0 ?EPP1");
softly.assertThat(extractor.getParameterMap().entrySet()) //
.extracting(Map.Entry::getKey, Map.Entry::getValue) //
.containsExactlyInAnyOrder( //
Tuple.tuple("EPP0", "one"), //
Tuple.tuple("EPP1", "two") //
);
softly.assertAll();
}
@Test // DATACMNS-1258
void keepsStringWhenNoMatchIsFound() {
var context = SpelQueryContext.of(PARAMETER_NAME_SOURCE, REPLACEMENT_SOURCE);
var extractor = context.parse("abcdef");
softly.assertThat(extractor.getQueryString()).isEqualTo("abcdef");
softly.assertThat(extractor.getParameterMap()).isEmpty();
softly.assertAll();
}
@Test // DATACMNS-1258
void spelsInQuotesGetIgnored() {
var queries = Arrays.asList(//
"a'b:#{one}cd'ef", //
"a'b:#{o'ne}cdef", //
"ab':#{one}'cdef", //
"ab:'#{one}cd'ef", //
"ab:#'{one}cd'ef", //
"a'b:#{o'ne}cdef");
queries.forEach(this::checkNoSpelIsFound);
softly.assertAll();
}
private void checkNoSpelIsFound(String query) {
var context = SpelQueryContext.of(PARAMETER_NAME_SOURCE, REPLACEMENT_SOURCE);
var extractor = context.parse(query);
softly.assertThat(extractor.getQueryString()).describedAs(query).isEqualTo(query);
softly.assertThat(extractor.getParameterMap()).describedAs(query).isEmpty();
}
}

View File

@@ -1,82 +0,0 @@
/*
* Copyright 2018-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.repository.query;
import static org.assertj.core.api.Assertions.*;
import java.util.function.BiFunction;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link SpelQueryContext}.
*
* @author Oliver Gierke
* @author Jens Schauder
* @author Mark Paluch
*/
class SpelQueryContextUnitTests {
static final QueryMethodEvaluationContextProvider EVALUATION_CONTEXT_PROVIDER = QueryMethodEvaluationContextProvider.DEFAULT;
static final BiFunction<Integer, String, String> PARAMETER_NAME_SOURCE = (index, spel) -> "__$synthetic$__" + index;
static final BiFunction<String, String, String> REPLACEMENT_SOURCE = (prefix, name) -> prefix + name;
@Test // DATACMNS-1258
void nullParameterNameSourceThrowsException() {
assertThatIllegalArgumentException() //
.isThrownBy(() -> SpelQueryContext.of(null, REPLACEMENT_SOURCE));
}
@Test // DATACMNS-1258
void nullReplacementSourceThrowsException() {
assertThatIllegalArgumentException() //
.isThrownBy(() -> SpelQueryContext.of(PARAMETER_NAME_SOURCE, null));
}
@Test // DATACMNS-1258
void rejectsNullEvaluationContextProvider() {
var context = SpelQueryContext.of(PARAMETER_NAME_SOURCE, REPLACEMENT_SOURCE);
assertThatIllegalArgumentException() //
.isThrownBy(() -> context.withEvaluationContextProvider(null));
}
@Test // DATACMNS-1258
void createsEvaluatingContextUsingProvider() {
var context = SpelQueryContext.of(PARAMETER_NAME_SOURCE, REPLACEMENT_SOURCE);
assertThat(context.withEvaluationContextProvider(EVALUATION_CONTEXT_PROVIDER)).isNotNull();
}
@Test // DATACMNS-1683, GH-
void reportsQuotationCorrectly() {
var context = SpelQueryContext.of(PARAMETER_NAME_SOURCE, REPLACEMENT_SOURCE);
var extractor = context.parse(
"select n from NetworkServer n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',:#{#networkRequest.name},'%')), '')) OR :#{#networkRequest.name} IS NULL )");
assertThat(extractor.getQueryString()).isEqualTo(
"select n from NetworkServer n where (LOWER(n.name) LIKE LOWER(NULLIF(text(concat('%',:__$synthetic$__0,'%')), '')) OR :__$synthetic$__1 IS NULL )");
assertThat(extractor.isQuoted(extractor.getQueryString().indexOf(":__$synthetic$__0"))).isFalse();
assertThat(extractor.isQuoted(extractor.getQueryString().indexOf(":__$synthetic$__1"))).isFalse();
assertThat(extractor.size()).isEqualTo(2);
}
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright 2018-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.type.classreading;
import static org.assertj.core.api.Assertions.*;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import org.springframework.core.type.MethodMetadata;
import org.springframework.data.type.MethodsMetadata;
/**
* Unit tests for {@link DefaultMethodsMetadataReader}.
*
* @author Mark Paluch
*/
class DefaultMethodsMetadataReaderUnitTests {
@Test // DATACMNS-1206
void shouldReadClassMethods() throws IOException {
var metadata = getMethodsMetadata(Foo.class);
assertThat(metadata.getMethods()).hasSize(3);
var iterator = metadata.getMethods().iterator();
assertThat(iterator.next().getMethodName()).isEqualTo("one");
assertThat(iterator.next().getMethodName()).isEqualTo("two");
assertThat(iterator.next().getMethodName()).isEqualTo("three");
}
@Test // DATACMNS-1206
void shouldReadInterfaceMethods() throws IOException {
var metadata = getMethodsMetadata(Baz.class);
assertThat(metadata.getMethods()).hasSize(3);
var iterator = metadata.getMethods().iterator();
assertThat(iterator.next().getMethodName()).isEqualTo("one");
assertThat(iterator.next().getMethodName()).isEqualTo("two");
assertThat(iterator.next().getMethodName()).isEqualTo("three");
}
@Test // DATACMNS-1206
void shouldMetadata() throws IOException {
var factory = new MethodsMetadataReaderFactory();
var metadataReader = factory.getMetadataReader(getClass().getName());
assertThat(metadataReader.getClassMetadata()).isNotNull();
assertThat(metadataReader.getAnnotationMetadata()).isNotNull();
}
@Test // DATACMNS-1206
void shouldReturnMethodMetadataByName() throws IOException {
var metadata = getMethodsMetadata(Foo.class);
assertThat(metadata.getMethods()).hasSize(3);
assertThat(metadata.getMethods("one")).extracting(MethodMetadata::getMethodName).contains("one");
assertThat(metadata.getMethods("foo")).isEmpty();
}
private static MethodsMetadata getMethodsMetadata(Class<?> classToIntrospect) throws IOException {
var factory = new MethodsMetadataReaderFactory();
var metadataReader = factory.getMetadataReader(classToIntrospect.getName());
return metadataReader.getMethodsMetadata();
}
// Create a scenario with a cyclic dependency to mix up methods reported by class.getDeclaredMethods()
// That's not exactly deterministic because it depends on when the compiler sees the classes.
abstract class Foo {
abstract void one(Foo b);
abstract void two(Bar b);
abstract void three(Foo b);
}
interface Baz {
void one(Foo b);
void two(Bar b);
void three(Baz b);
}
abstract class Bar {
abstract void dependOnFoo(Foo f);
abstract void dependOnBaz(Baz f);
}
}

View File

@@ -1,69 +0,0 @@
/*
* Copyright 2018-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.type.classreading;
import static org.assertj.core.api.Assertions.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.DefaultResourceLoader;
/**
* Unit tests for {@link MethodsMetadataReaderFactory}.
*
* @author Mark Paluch
*/
class MethodsMetadataReaderFactoryUnitTests {
@Test // DATACMNS-1206
void shouldReadFromDefaultClassLoader() throws IOException {
var factory = new MethodsMetadataReaderFactory();
var reader = factory.getMetadataReader(getClass().getName());
assertThat(reader).isNotNull();
}
@Test // DATACMNS-1206
void shouldReadFromClassLoader() throws IOException {
var factory = new MethodsMetadataReaderFactory(getClass().getClassLoader());
var reader = factory.getMetadataReader(getClass().getName());
assertThat(reader).isNotNull();
}
@Test // DATACMNS-1206
void shouldNotFindClass() {
var factory = new MethodsMetadataReaderFactory(new URLClassLoader(new URL[0], null));
assertThatThrownBy(() -> factory.getMetadataReader(getClass().getName())).isInstanceOf(FileNotFoundException.class);
}
@Test // DATACMNS-1206
void shouldReadFromResourceLoader() throws IOException {
var factory = new MethodsMetadataReaderFactory(new DefaultResourceLoader());
var reader = factory.getMetadataReader(getClass().getName());
assertThat(reader).isNotNull();
}
}

View File

@@ -20,12 +20,9 @@ import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.Test
import org.springframework.data.annotation.PersistenceConstructor
import org.springframework.data.annotation.Persistent
import org.springframework.data.annotation.PersistenceCreator
import org.springframework.data.mapping.PersistentEntity
import org.springframework.data.mapping.context.SamplePersistentProperty
import org.springframework.data.mapping.model.KotlinValueUtils.BoxingRules
import kotlin.jvm.internal.Reflection
import kotlin.reflect.KClass
/**
@@ -297,13 +294,16 @@ class KotlinClassGeneratingEntityInstantiatorUnitTests {
)
data class WithConstructorsHavingSameParameterCount @PersistenceConstructor constructor(val id: Long?, val notes: Map<String, String> = emptyMap()) {
data class WithConstructorsHavingSameParameterCount @PersistenceCreator constructor(
val id: Long?,
val notes: Map<String, String> = emptyMap()
) {
constructor(notes: Map<String, String>, additionalNotes: Map<String, String> = emptyMap()) : this(null, notes + additionalNotes)
}
data class ContactWithPersistenceConstructor(val firstname: String, val lastname: String) {
@PersistenceConstructor
@PersistenceCreator
constructor(firstname: String) : this(firstname, "")
}
@@ -312,7 +312,7 @@ class KotlinClassGeneratingEntityInstantiatorUnitTests {
var organisations: MutableList<Organisation> = mutableListOf()
) {
@PersistenceConstructor
@PersistenceCreator
constructor(id: String?) : this(id, mutableListOf())
}

View File

@@ -17,7 +17,7 @@ package org.springframework.data.mapping.model
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.data.annotation.PersistenceConstructor
import org.springframework.data.annotation.PersistenceCreator
import org.springframework.data.mapping.model.AbstractPersistentPropertyUnitTests.SamplePersistentProperty
/**
@@ -129,7 +129,7 @@ class PreferredConstructorDiscovererUnitTests {
class AnnotatedConstructors(val firstname: String) {
@PersistenceConstructor
@PersistenceCreator
constructor(firstname: String, lastname: String) : this(firstname)
}
@@ -140,7 +140,7 @@ class PreferredConstructorDiscovererUnitTests {
val lastname: String = "bar"
) {
@PersistenceConstructor
@PersistenceCreator
constructor(firstname: String = "foo", lastname: String = "bar", age: Int) : this(
firstname,
lastname

View File

@@ -19,6 +19,7 @@ import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.core.DefaultParameterNameDiscoverer
import org.springframework.core.MethodParameter
import org.springframework.data.util.TypeInformation
import kotlin.reflect.jvm.javaMethod
/**
@@ -34,7 +35,7 @@ class KParameterUnitTests {
val methodParameter =
MethodParameter(MyCoroutineRepository::hello.javaMethod!!, 0)
methodParameter.initParameterNameDiscovery(DefaultParameterNameDiscoverer())
val parameter = Parameter(methodParameter)
val parameter = Parameter(methodParameter, TypeInformation.OBJECT)
assertThat(parameter.name).isEmpty()
assertThat(parameter.isBindable).isFalse()