Use full type information for identifier and domain types exposed byRepositoryMetadata.
See #2518.
This commit is contained in:
committed by
Oliver Drotbohm
parent
21e4fd5ffb
commit
58ed9ce211
@@ -27,29 +27,48 @@ import org.springframework.data.util.TypeInformation;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
* @author Alessandro Nistico
|
||||
*/
|
||||
public interface RepositoryMetadata {
|
||||
|
||||
/**
|
||||
* Returns the id class the given class is declared for.
|
||||
* Returns the id {@link TypeInformation} the given class is declared for.
|
||||
*
|
||||
* @return the id class of the entity managed by the repository.
|
||||
* @return the {@link TypeInformation} class of the entity managed by the repository.
|
||||
*/
|
||||
Class<?> getIdType();
|
||||
TypeInformation<?> getIdTypeInformation();
|
||||
|
||||
/**
|
||||
* Returns the domain class the repository is declared for.
|
||||
* Returns the domain {@link TypeInformation} the repository is declared for.
|
||||
*
|
||||
* @return the domain class the repository is handling.
|
||||
*/
|
||||
Class<?> getDomainType();
|
||||
|
||||
TypeInformation<?> getDomainTypeInformation();
|
||||
|
||||
/**
|
||||
* Returns the repository interface.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?> getRepositoryInterface();
|
||||
|
||||
/**
|
||||
* Returns the raw id class the given class is declared for.
|
||||
*
|
||||
* @return the raw id class of the entity managed by the repository.
|
||||
*/
|
||||
default Class<?> getIdType() {
|
||||
return getIdTypeInformation().getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw domain class the repository is declared for.
|
||||
*
|
||||
* @return the raw domain class the repository is handling.
|
||||
*/
|
||||
default Class<?> getDomainType() {
|
||||
return getDomainTypeInformation().getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type {@link Method} return type as it is declared in the repository. Considers suspended methods and
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.data.repository.core.support;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.repository.RepositoryDefinition;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -27,14 +29,15 @@ import org.springframework.util.Assert;
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Xeno Amess
|
||||
* @author Alessandro Nistico
|
||||
*/
|
||||
public class AnnotationRepositoryMetadata extends AbstractRepositoryMetadata {
|
||||
|
||||
private static final String NO_ANNOTATION_FOUND = String.format("Interface %%s must be annotated with @%s!",
|
||||
RepositoryDefinition.class.getName());
|
||||
|
||||
private final Class<?> idType;
|
||||
private final Class<?> domainType;
|
||||
private final TypeInformation<?> idType;
|
||||
private final TypeInformation<?> domainType;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AnnotationRepositoryMetadata} instance looking up repository types from a
|
||||
@@ -54,16 +57,16 @@ public class AnnotationRepositoryMetadata extends AbstractRepositoryMetadata {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getIdType() {
|
||||
public TypeInformation<?> getIdTypeInformation() {
|
||||
return this.idType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getDomainType() {
|
||||
public TypeInformation<?> getDomainTypeInformation() {
|
||||
return this.domainType;
|
||||
}
|
||||
|
||||
private Class<?> resolveIdType(Class<?> repositoryInterface) {
|
||||
private TypeInformation<?> resolveIdType(Class<?> repositoryInterface) {
|
||||
|
||||
RepositoryDefinition annotation = AnnotationUtils.findAnnotation(repositoryInterface, RepositoryDefinition.class);
|
||||
|
||||
@@ -71,10 +74,10 @@ public class AnnotationRepositoryMetadata extends AbstractRepositoryMetadata {
|
||||
throw new IllegalArgumentException(String.format("Could not resolve id type of %s!", repositoryInterface));
|
||||
}
|
||||
|
||||
return annotation.idClass();
|
||||
return ClassTypeInformation.from(annotation.idClass());
|
||||
}
|
||||
|
||||
private Class<?> resolveDomainType(Class<?> repositoryInterface) {
|
||||
private TypeInformation<?> resolveDomainType(Class<?> repositoryInterface) {
|
||||
|
||||
RepositoryDefinition annotation = AnnotationUtils.findAnnotation(repositoryInterface, RepositoryDefinition.class);
|
||||
|
||||
@@ -82,6 +85,6 @@ public class AnnotationRepositoryMetadata extends AbstractRepositoryMetadata {
|
||||
throw new IllegalArgumentException(String.format("Could not resolve domain type of %s!", repositoryInterface));
|
||||
}
|
||||
|
||||
return annotation.domainClass();
|
||||
return ClassTypeInformation.from(annotation.domainClass());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Thomas Darimont
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @author Alessandro Nistico
|
||||
*/
|
||||
class DefaultRepositoryInformation implements RepositoryInformation {
|
||||
|
||||
@@ -76,13 +77,13 @@ class DefaultRepositoryInformation implements RepositoryInformation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getDomainType() {
|
||||
return metadata.getDomainType();
|
||||
public TypeInformation<?> getDomainTypeInformation() {
|
||||
return metadata.getDomainTypeInformation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getIdType() {
|
||||
return metadata.getIdType();
|
||||
public TypeInformation<?> getIdTypeInformation() {
|
||||
return metadata.getIdTypeInformation();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,14 +30,15 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Alessandro Nistico
|
||||
*/
|
||||
public class DefaultRepositoryMetadata extends AbstractRepositoryMetadata {
|
||||
|
||||
private static final String MUST_BE_A_REPOSITORY = String.format("Given type must be assignable to %s!",
|
||||
Repository.class);
|
||||
|
||||
private final Class<?> idType;
|
||||
private final Class<?> domainType;
|
||||
private final TypeInformation<?> idType;
|
||||
private final TypeInformation<?> domainType;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultRepositoryMetadata} for the given repository interface.
|
||||
@@ -49,31 +50,32 @@ public class DefaultRepositoryMetadata extends AbstractRepositoryMetadata {
|
||||
super(repositoryInterface);
|
||||
Assert.isTrue(Repository.class.isAssignableFrom(repositoryInterface), MUST_BE_A_REPOSITORY);
|
||||
|
||||
List<TypeInformation<?>> arguments = ClassTypeInformation.from(repositoryInterface) //
|
||||
List<TypeInformation<?>> arguments = ClassTypeInformation.from(repositoryInterface)//
|
||||
.getRequiredSuperTypeInformation(Repository.class)//
|
||||
.getTypeArguments();
|
||||
|
||||
this.domainType = resolveTypeParameter(arguments, 0,
|
||||
() -> String.format("Could not resolve domain type of %s!", repositoryInterface));
|
||||
|
||||
this.idType = resolveTypeParameter(arguments, 1,
|
||||
() -> String.format("Could not resolve id type of %s!", repositoryInterface));
|
||||
}
|
||||
|
||||
private static Class<?> resolveTypeParameter(List<TypeInformation<?>> arguments, int index,
|
||||
private static TypeInformation<?> resolveTypeParameter(List<TypeInformation<?>> arguments, int index,
|
||||
Supplier<String> exceptionMessage) {
|
||||
|
||||
if (arguments.size() <= index || arguments.get(index) == null) {
|
||||
throw new IllegalArgumentException(exceptionMessage.get());
|
||||
}
|
||||
|
||||
return arguments.get(index).getType();
|
||||
return arguments.get(index).getGenericTypeInformation();
|
||||
}
|
||||
|
||||
public Class<?> getIdType() {
|
||||
public TypeInformation<?> getIdTypeInformation() {
|
||||
return this.idType;
|
||||
}
|
||||
|
||||
public Class<?> getDomainType() {
|
||||
public TypeInformation<?> getDomainTypeInformation() {
|
||||
return this.domainType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.springframework.util.ObjectUtils;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
* @author Alessandro Nistico
|
||||
* @since 2.0
|
||||
*/
|
||||
interface MethodLookups {
|
||||
@@ -120,8 +121,8 @@ interface MethodLookups {
|
||||
|
||||
Assert.notNull(repositoryMetadata, "Repository metadata must not be null!");
|
||||
|
||||
this.entityType = ResolvableType.forClass(repositoryMetadata.getDomainType());
|
||||
this.idType = ResolvableType.forClass(repositoryMetadata.getIdType());
|
||||
this.entityType = ResolvableType.forType(repositoryMetadata.getDomainTypeInformation().getGenericType());
|
||||
this.idType = ResolvableType.forType(repositoryMetadata.getIdTypeInformation().getGenericType());
|
||||
this.repositoryInterface = repositoryMetadata.getRepositoryInterface();
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConditionalGenericConverter;
|
||||
@@ -29,6 +30,7 @@ import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -42,6 +44,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Alessandro Nistico
|
||||
*/
|
||||
public class DomainClassConverter<T extends ConversionService & ConverterRegistry>
|
||||
implements ConditionalGenericConverter, ApplicationContextAware {
|
||||
@@ -101,6 +104,12 @@ public class DomainClassConverter<T extends ConversionService & ConverterRegistr
|
||||
return repositories;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static TypeDescriptor getIdTypeDescriptor(RepositoryInformation information) {
|
||||
TypeInformation<?> idType = information.getIdTypeInformation();
|
||||
return new TypeDescriptor(ResolvableType.forType(idType.getGenericType()), null, idType.getType().getAnnotations());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converter to create domain types from any source that can be converted into the domain types identifier type.
|
||||
@@ -148,8 +157,9 @@ public class DomainClassConverter<T extends ConversionService & ConverterRegistr
|
||||
Class<?> domainType = targetType.getType();
|
||||
RepositoryInvoker invoker = repositoryInvokerFactory.getInvokerFor(domainType);
|
||||
RepositoryInformation information = repositories.getRequiredRepositoryInformation(domainType);
|
||||
TypeDescriptor idTypeDescriptor = getIdTypeDescriptor(information);
|
||||
|
||||
Object id = conversionService.convert(source, information.getIdType());
|
||||
Object id = conversionService.convert(source, sourceType, idTypeDescriptor);
|
||||
|
||||
return id == null ? null : invoker.invokeFindById(id).orElse(null);
|
||||
}
|
||||
@@ -171,10 +181,10 @@ public class DomainClassConverter<T extends ConversionService & ConverterRegistr
|
||||
|
||||
return repositoryInformation.map(it -> {
|
||||
|
||||
Class<?> rawIdType = it.getIdType();
|
||||
TypeDescriptor idTypeDescriptor = getIdTypeDescriptor(it);
|
||||
|
||||
return sourceType.equals(TypeDescriptor.valueOf(rawIdType))
|
||||
|| conversionService.canConvert(sourceType.getType(), rawIdType);
|
||||
return sourceType.equals(idTypeDescriptor)
|
||||
|| conversionService.canConvert(sourceType, idTypeDescriptor);
|
||||
}).orElseThrow(
|
||||
() -> new IllegalStateException(String.format("Couldn't find RepositoryInformation for %s!", domainType)));
|
||||
}
|
||||
@@ -218,8 +228,8 @@ public class DomainClassConverter<T extends ConversionService & ConverterRegistr
|
||||
Class<?> domainType = sourceType.getType();
|
||||
|
||||
EntityInformation<Object, ?> entityInformation = repositories.getEntityInformationFor(domainType);
|
||||
|
||||
return conversionService.convert(entityInformation.getId(source), targetType.getType());
|
||||
Object id = entityInformation.getId(source);
|
||||
return conversionService.convert(id, TypeDescriptor.forObject(id), targetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -239,10 +249,10 @@ public class DomainClassConverter<T extends ConversionService & ConverterRegistr
|
||||
|
||||
return information.map(it -> {
|
||||
|
||||
Class<?> rawIdType = it.getIdType();
|
||||
TypeDescriptor idTypeDescriptor = getIdTypeDescriptor(it);
|
||||
|
||||
return targetType.equals(TypeDescriptor.valueOf(rawIdType))
|
||||
|| conversionService.canConvert(rawIdType, targetType.getType());
|
||||
return targetType.equals(idTypeDescriptor)
|
||||
|| conversionService.canConvert(idTypeDescriptor, targetType);
|
||||
|
||||
}).orElseThrow(
|
||||
() -> new IllegalStateException(String.format("Couldn't find RepositoryInformation for %s!", domainType)));
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.ConversionException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
@@ -30,6 +31,7 @@ import org.springframework.data.repository.core.CrudMethods;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.repository.util.QueryExecutionConverters;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -41,6 +43,7 @@ import org.springframework.util.StringUtils;
|
||||
* Base {@link RepositoryInvoker} using reflection to invoke methods on Spring Data Repositories.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Alessandro Nistico
|
||||
* @since 1.10
|
||||
*/
|
||||
class ReflectionRepositoryInvoker implements RepositoryInvoker {
|
||||
@@ -50,7 +53,7 @@ class ReflectionRepositoryInvoker implements RepositoryInvoker {
|
||||
|
||||
private final Object repository;
|
||||
private final CrudMethods methods;
|
||||
private final Class<?> idType;
|
||||
private final TypeDescriptor idTypeDescriptor;
|
||||
private final ConversionService conversionService;
|
||||
|
||||
/**
|
||||
@@ -70,7 +73,8 @@ class ReflectionRepositoryInvoker implements RepositoryInvoker {
|
||||
|
||||
this.repository = repository;
|
||||
this.methods = metadata.getCrudMethods();
|
||||
this.idType = metadata.getIdType();
|
||||
TypeInformation<?> idType = metadata.getIdTypeInformation();
|
||||
this.idTypeDescriptor = new TypeDescriptor(ResolvableType.forType(idType.getGenericType()), null, idType.getType().getAnnotations());
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@@ -245,16 +249,17 @@ class ReflectionRepositoryInvoker implements RepositoryInvoker {
|
||||
protected Object convertId(Object id) {
|
||||
|
||||
Assert.notNull(id, "Id must not be null!");
|
||||
TypeDescriptor idDescriptor = TypeDescriptor.forObject(id);
|
||||
|
||||
if (idType.isInstance(id)) {
|
||||
if (idDescriptor.isAssignableTo(idTypeDescriptor)) {
|
||||
return id;
|
||||
}
|
||||
|
||||
Object result = conversionService.convert(id, idType);
|
||||
Object result = conversionService.convert(id, idDescriptor, idTypeDescriptor);
|
||||
|
||||
if (result == null) {
|
||||
throw new IllegalStateException(
|
||||
String.format("Identifier conversion of %s to %s unexpectedly returned null!", id, idType));
|
||||
String.format("Identifier conversion of %s to %s unexpectedly returned null!", id, idTypeDescriptor.getType()));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.springframework.util.ConcurrentLruCache;
|
||||
* @author Thomas Darimont
|
||||
* @author Thomas Eizinger
|
||||
* @author Christoph Strobl
|
||||
* @author Alessandro Nistico
|
||||
*/
|
||||
public class Repositories implements Iterable<Class<?>> {
|
||||
|
||||
@@ -102,10 +103,9 @@ public class Repositories implements Iterable<Class<?>> {
|
||||
|
||||
RepositoryFactoryInformation repositoryFactoryInformation = beanFactory.get().getBean(name,
|
||||
RepositoryFactoryInformation.class);
|
||||
Class<?> domainType = ClassUtils
|
||||
.getUserClass(repositoryFactoryInformation.getRepositoryInformation().getDomainType());
|
||||
|
||||
RepositoryInformation information = repositoryFactoryInformation.getRepositoryInformation();
|
||||
Class<?> domainType = ClassUtils.getUserClass(information.getDomainType());
|
||||
|
||||
Set<Class<?>> alternativeDomainTypes = information.getAlternativeDomainTypes();
|
||||
|
||||
Set<Class<?>> typesToRegister = new HashSet<>(alternativeDomainTypes.size() + 1);
|
||||
|
||||
@@ -53,6 +53,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Jürgen Diez
|
||||
* @author Alessandro Nistico
|
||||
*/
|
||||
class TypeDiscoverer<S> implements TypeInformation<S> {
|
||||
|
||||
@@ -293,6 +294,16 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
|
||||
public Class<S> getType() {
|
||||
return resolvedType.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getGenericType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeInformation<?> getGenericTypeInformation() {
|
||||
return createInfo(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassTypeInformation<?> getRawTypeInformation() {
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.util;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -27,6 +28,7 @@ import org.springframework.lang.Nullable;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
* @author Alessandro Nistico
|
||||
*/
|
||||
public interface TypeInformation<S> {
|
||||
|
||||
@@ -148,6 +150,15 @@ public interface TypeInformation<S> {
|
||||
* @return
|
||||
*/
|
||||
Class<S> getType();
|
||||
|
||||
/**
|
||||
* Returns the type of the property with all resolvable generics applied
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
default Type getGenericType() {
|
||||
return getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user type of the property if proxied.
|
||||
@@ -170,6 +181,13 @@ public interface TypeInformation<S> {
|
||||
*/
|
||||
ClassTypeInformation<?> getRawTypeInformation();
|
||||
|
||||
/**
|
||||
* Returns a {@link TypeInformation} to represent the {@link TypeInformation} of the type of the current instance with all the generics parameters resolved.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
TypeInformation<?> getGenericTypeInformation();
|
||||
|
||||
/**
|
||||
* Transparently returns the {@link java.util.Map} value type if the type is a {@link java.util.Map}, returns the
|
||||
* component type if the type {@link #isCollectionLike()} or the simple type if none of this applies.
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.util.Assert;
|
||||
* {@link TypeVariable} is being used in.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Alessandro Nistico
|
||||
*/
|
||||
class TypeVariableTypeInformation<T> extends ParentTypeAwareTypeInformation<T> {
|
||||
|
||||
@@ -49,6 +50,11 @@ class TypeVariableTypeInformation<T> extends ParentTypeAwareTypeInformation<T> {
|
||||
|
||||
this.variable = variable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeInformation<?> getGenericTypeInformation() {
|
||||
return createInfo(getTypeVariableMap().getOrDefault(variable, Object.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
|
||||
Reference in New Issue
Block a user