@@ -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 {}
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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}.
|
||||
*
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
* <prefix>#{<spel>}
|
||||
* </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>
|
||||
* <prefix>#{<spel>}
|
||||
* </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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
* Core support package for type introspection.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.data.type;
|
||||
@@ -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;
|
||||
|
||||
@@ -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}.
|
||||
*
|
||||
|
||||
@@ -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}.
|
||||
*
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user