From 58ed9ce211c295477d193c8dfefa392338606f18 Mon Sep 17 00:00:00 2001 From: Alex Nistico Date: Fri, 31 Dec 2021 05:43:17 +0100 Subject: [PATCH] Use full type information for identifier and domain types exposed byRepositoryMetadata. See #2518. --- .../repository/core/RepositoryMetadata.java | 31 +++++++++--- .../support/AnnotationRepositoryMetadata.java | 19 ++++--- .../support/DefaultRepositoryInformation.java | 9 ++-- .../support/DefaultRepositoryMetadata.java | 16 +++--- .../core/support/MethodLookups.java | 5 +- .../support/DomainClassConverter.java | 28 +++++++---- .../support/ReflectionRepositoryInvoker.java | 15 ++++-- .../data/repository/support/Repositories.java | 6 +-- .../data/util/TypeDiscoverer.java | 11 +++++ .../data/util/TypeInformation.java | 18 +++++++ .../util/TypeVariableTypeInformation.java | 6 +++ .../AbstractRepositoryMetadataUnitTests.java | 49 ++++++------------- .../DefaultRepositoryMetadataUnitTests.java | 7 ++- .../support/DummyRepositoryInformation.java | 8 +-- .../DomainClassConverterIntegrationTests.java | 7 ++- .../DomainClassConverterUnitTests.java | 9 ++-- .../support/RepositoriesUnitTests.java | 9 ++-- 17 files changed, 161 insertions(+), 92 deletions(-) diff --git a/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java b/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java index 6a1d9ea1b..136630e81 100644 --- a/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java +++ b/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java @@ -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 diff --git a/src/main/java/org/springframework/data/repository/core/support/AnnotationRepositoryMetadata.java b/src/main/java/org/springframework/data/repository/core/support/AnnotationRepositoryMetadata.java index 7732e7cbb..8bb9d4607 100644 --- a/src/main/java/org/springframework/data/repository/core/support/AnnotationRepositoryMetadata.java +++ b/src/main/java/org/springframework/data/repository/core/support/AnnotationRepositoryMetadata.java @@ -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()); } } diff --git a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java index 270cbd8ec..35a9cdec1 100644 --- a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java +++ b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java @@ -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 diff --git a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryMetadata.java b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryMetadata.java index dd28d4d1f..48ee763f1 100644 --- a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryMetadata.java +++ b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryMetadata.java @@ -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> arguments = ClassTypeInformation.from(repositoryInterface) // + List> 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> arguments, int index, + private static TypeInformation resolveTypeParameter(List> arguments, int index, Supplier 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; } } diff --git a/src/main/java/org/springframework/data/repository/core/support/MethodLookups.java b/src/main/java/org/springframework/data/repository/core/support/MethodLookups.java index e8c967a92..e9812ccf2 100644 --- a/src/main/java/org/springframework/data/repository/core/support/MethodLookups.java +++ b/src/main/java/org/springframework/data/repository/core/support/MethodLookups.java @@ -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(); } diff --git a/src/main/java/org/springframework/data/repository/support/DomainClassConverter.java b/src/main/java/org/springframework/data/repository/support/DomainClassConverter.java index cea38ccd1..2471ca52c 100644 --- a/src/main/java/org/springframework/data/repository/support/DomainClassConverter.java +++ b/src/main/java/org/springframework/data/repository/support/DomainClassConverter.java @@ -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 implements ConditionalGenericConverter, ApplicationContextAware { @@ -101,6 +104,12 @@ public class DomainClassConverter 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 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 { - 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 domainType = sourceType.getType(); EntityInformation 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 { - 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))); diff --git a/src/main/java/org/springframework/data/repository/support/ReflectionRepositoryInvoker.java b/src/main/java/org/springframework/data/repository/support/ReflectionRepositoryInvoker.java index a126466a6..1e7a187d9 100644 --- a/src/main/java/org/springframework/data/repository/support/ReflectionRepositoryInvoker.java +++ b/src/main/java/org/springframework/data/repository/support/ReflectionRepositoryInvoker.java @@ -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; diff --git a/src/main/java/org/springframework/data/repository/support/Repositories.java b/src/main/java/org/springframework/data/repository/support/Repositories.java index f72fccc0c..49d4cf897 100644 --- a/src/main/java/org/springframework/data/repository/support/Repositories.java +++ b/src/main/java/org/springframework/data/repository/support/Repositories.java @@ -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> { @@ -102,10 +103,9 @@ public class Repositories implements Iterable> { 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> alternativeDomainTypes = information.getAlternativeDomainTypes(); Set> typesToRegister = new HashSet<>(alternativeDomainTypes.size() + 1); diff --git a/src/main/java/org/springframework/data/util/TypeDiscoverer.java b/src/main/java/org/springframework/data/util/TypeDiscoverer.java index a3beb8d8c..1e7e50e12 100644 --- a/src/main/java/org/springframework/data/util/TypeDiscoverer.java +++ b/src/main/java/org/springframework/data/util/TypeDiscoverer.java @@ -53,6 +53,7 @@ import org.springframework.util.ReflectionUtils; * @author Christoph Strobl * @author Mark Paluch * @author Jürgen Diez + * @author Alessandro Nistico */ class TypeDiscoverer implements TypeInformation { @@ -293,6 +294,16 @@ class TypeDiscoverer implements TypeInformation { public Class getType() { return resolvedType.get(); } + + @Override + public Type getGenericType() { + return type; + } + + @Override + public TypeInformation getGenericTypeInformation() { + return createInfo(type); + } @Override public ClassTypeInformation getRawTypeInformation() { diff --git a/src/main/java/org/springframework/data/util/TypeInformation.java b/src/main/java/org/springframework/data/util/TypeInformation.java index 94e1f5d8a..6df89ff3c 100644 --- a/src/main/java/org/springframework/data/util/TypeInformation.java +++ b/src/main/java/org/springframework/data/util/TypeInformation.java @@ -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 { @@ -148,6 +150,15 @@ public interface TypeInformation { * @return */ Class 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 { */ 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. diff --git a/src/main/java/org/springframework/data/util/TypeVariableTypeInformation.java b/src/main/java/org/springframework/data/util/TypeVariableTypeInformation.java index dbc587d41..d1fc1935c 100644 --- a/src/main/java/org/springframework/data/util/TypeVariableTypeInformation.java +++ b/src/main/java/org/springframework/data/util/TypeVariableTypeInformation.java @@ -28,6 +28,7 @@ import org.springframework.util.Assert; * {@link TypeVariable} is being used in. * * @author Oliver Gierke + * @author Alessandro Nistico */ class TypeVariableTypeInformation extends ParentTypeAwareTypeInformation { @@ -49,6 +50,11 @@ class TypeVariableTypeInformation extends ParentTypeAwareTypeInformation { this.variable = variable; } + + @Override + public TypeInformation getGenericTypeInformation() { + return createInfo(getTypeVariableMap().getOrDefault(variable, Object.class)); + } @Override public boolean equals(@Nullable Object obj) { diff --git a/src/test/java/org/springframework/data/repository/core/support/AbstractRepositoryMetadataUnitTests.java b/src/test/java/org/springframework/data/repository/core/support/AbstractRepositoryMetadataUnitTests.java index 988a288f3..939fea220 100755 --- a/src/test/java/org/springframework/data/repository/core/support/AbstractRepositoryMetadataUnitTests.java +++ b/src/test/java/org/springframework/data/repository/core/support/AbstractRepositoryMetadataUnitTests.java @@ -18,16 +18,14 @@ package org.springframework.data.repository.core.support; import static org.assertj.core.api.Assertions.*; import java.io.Serializable; +import java.lang.reflect.Method; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; - -import org.springframework.core.ResolvableType; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.querydsl.User; -import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.RepositoryMetadata; @@ -38,58 +36,59 @@ import org.springframework.data.repository.core.RepositoryMetadata; * @author Oliver Gierke * @author Thomas Darimont * @author Fabian Buch + * @author Alessandro Nistico */ class AbstractRepositoryMetadataUnitTests { @Test // DATACMNS-98 void discoversSimpleReturnTypeCorrectly() throws Exception { - RepositoryMetadata metadata = new DummyRepositoryMetadata(UserRepository.class); - var method = UserRepository.class.getMethod("findSingle"); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); + Method method = UserRepository.class.getMethod("findSingle"); assertThat(metadata.getReturnedDomainClass(method)).isEqualTo(User.class); } @Test // DATACMNS-98 void resolvesTypeParameterReturnType() throws Exception { - RepositoryMetadata metadata = new DummyRepositoryMetadata(ConcreteRepository.class); - var method = ConcreteRepository.class.getMethod("intermediateMethod"); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(ConcreteRepository.class); + Method method = ConcreteRepository.class.getMethod("intermediateMethod"); assertThat(metadata.getReturnedDomainClass(method)).isEqualTo(User.class); } @Test // DATACMNS-98 void determinesReturnTypeFromPageable() throws Exception { - RepositoryMetadata metadata = new DummyRepositoryMetadata(ExtendingRepository.class); - var method = ExtendingRepository.class.getMethod("findByFirstname", Pageable.class, String.class); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(ExtendingRepository.class); + Method method = ExtendingRepository.class.getMethod("findByFirstname", Pageable.class, String.class); assertThat(metadata.getReturnedDomainClass(method)).isEqualTo(User.class); } @Test // DATACMNS-453 void nonPageableRepository() { - RepositoryMetadata metadata = new DummyRepositoryMetadata(UserRepository.class); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class); assertThat(metadata.isPagingRepository()).isFalse(); } @Test // DATACMNS-453 void pageableRepository() { - RepositoryMetadata metadata = new DummyRepositoryMetadata(PagedRepository.class); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(PagedRepository.class); assertThat(metadata.isPagingRepository()).isTrue(); } @Test // DATACMNS-98 void determinesReturnTypeFromGenericType() throws Exception { - RepositoryMetadata metadata = new DummyRepositoryMetadata(ExtendingRepository.class); - var method = ExtendingRepository.class.getMethod("someMethod"); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(ExtendingRepository.class); + Method method = ExtendingRepository.class.getMethod("someMethod"); assertThat(metadata.getReturnedDomainClass(method)).isEqualTo(GenericType.class); } @Test // DATACMNS-98 void handlesGenericTypeInReturnedCollectionCorrectly() throws SecurityException, NoSuchMethodException { - RepositoryMetadata metadata = new DummyRepositoryMetadata(ExtendingRepository.class); - var method = ExtendingRepository.class.getMethod("anotherMethod"); + RepositoryMetadata metadata = new DefaultRepositoryMetadata(ExtendingRepository.class); + Method method = ExtendingRepository.class.getMethod("anotherMethod"); assertThat(metadata.getReturnedDomainClass(method)).isEqualTo(Map.class); } @@ -144,23 +143,6 @@ class AbstractRepositoryMetadataUnitTests { } - class DummyRepositoryMetadata extends AbstractRepositoryMetadata { - - DummyRepositoryMetadata(Class repositoryInterface) { - super(repositoryInterface); - } - - @SuppressWarnings("unchecked") - public Class getIdType() { - return (Class) ResolvableType// - .forClass(Repository.class, getRepositoryInterface()).getGeneric(1).resolve(); - } - - public Class getDomainType() { - return ResolvableType.forClass(Repository.class, getRepositoryInterface()).getGeneric(0).resolve(); - } - } - // DATACMNS-1299 class Element {} @@ -171,7 +153,6 @@ class AbstractRepositoryMetadataUnitTests { Container someMethod(); } - interface CompletePageableAndSortingRepository extends PagingAndSortingRepository { - } + interface CompletePageableAndSortingRepository extends PagingAndSortingRepository {} } diff --git a/src/test/java/org/springframework/data/repository/core/support/DefaultRepositoryMetadataUnitTests.java b/src/test/java/org/springframework/data/repository/core/support/DefaultRepositoryMetadataUnitTests.java index 794a18ec1..dfa673e69 100755 --- a/src/test/java/org/springframework/data/repository/core/support/DefaultRepositoryMetadataUnitTests.java +++ b/src/test/java/org/springframework/data/repository/core/support/DefaultRepositoryMetadataUnitTests.java @@ -29,12 +29,15 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.util.ClassUtils; +import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; /** * Unit tests for {@link DefaultRepositoryMetadata}. * * @author Oliver Gierke * @author Thomas Darimont + * @author Alessandro Nistico */ class DefaultRepositoryMetadataUnitTests { @@ -79,7 +82,9 @@ class DefaultRepositoryMetadataUnitTests { void detectsParameterizedEntitiesCorrectly() { RepositoryMetadata metadata = new DefaultRepositoryMetadata(GenericEntityRepository.class); - assertThat(metadata.getDomainType()).isEqualTo(GenericEntity.class); + TypeInformation domainType = metadata.getDomainTypeInformation(); + assertThat(domainType.getType()).isEqualTo(GenericEntity.class); + assertThat(domainType.getTypeArguments()).containsExactly(ClassTypeInformation.from(String.class)); } @Test diff --git a/src/test/java/org/springframework/data/repository/core/support/DummyRepositoryInformation.java b/src/test/java/org/springframework/data/repository/core/support/DummyRepositoryInformation.java index df2fd3a3d..b0dcd5e8c 100644 --- a/src/test/java/org/springframework/data/repository/core/support/DummyRepositoryInformation.java +++ b/src/test/java/org/springframework/data/repository/core/support/DummyRepositoryInformation.java @@ -36,12 +36,12 @@ public final class DummyRepositoryInformation implements RepositoryInformation { this.metadata = metadata; } - public Class getIdType() { - return metadata.getIdType(); + public TypeInformation getIdTypeInformation() { + return metadata.getIdTypeInformation(); } - public Class getDomainType() { - return metadata.getDomainType(); + public TypeInformation getDomainTypeInformation() { + return metadata.getDomainTypeInformation(); } public Class getRepositoryInterface() { diff --git a/src/test/java/org/springframework/data/repository/support/DomainClassConverterIntegrationTests.java b/src/test/java/org/springframework/data/repository/support/DomainClassConverterIntegrationTests.java index 98424bb7c..b7bb2dcca 100755 --- a/src/test/java/org/springframework/data/repository/support/DomainClassConverterIntegrationTests.java +++ b/src/test/java/org/springframework/data/repository/support/DomainClassConverterIntegrationTests.java @@ -37,11 +37,13 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.core.support.RepositoryFactoryInformation; +import org.springframework.data.util.ClassTypeInformation; /** * Integration test for {@link DomainClassConverter}. * * @author Oliver Gierke + * @author Alessandro Nistico */ @ExtendWith(MockitoExtension.class) class DomainClassConverterIntegrationTests { @@ -64,8 +66,9 @@ class DomainClassConverterIntegrationTests { beanFactory.registerBeanDefinition("postProcessor", new RootBeanDefinition(PredictingProcessor.class)); beanFactory.registerBeanDefinition("repoFactory", new RootBeanDefinition(RepositoryFactoryBeanSupport.class)); - doReturn(Person.class).when(information).getDomainType(); - doReturn(Serializable.class).when(information).getIdType(); + doReturn(ClassTypeInformation.from(Person.class)).when(information).getDomainTypeInformation(); + doReturn(ClassTypeInformation.from(Serializable.class)).when(information).getIdTypeInformation(); + doCallRealMethod().when(information).getDomainType(); doReturn(PersonRepository.class).when(factory).getObjectType(); doReturn(information).when(factory).getRepositoryInformation(); diff --git a/src/test/java/org/springframework/data/repository/support/DomainClassConverterUnitTests.java b/src/test/java/org/springframework/data/repository/support/DomainClassConverterUnitTests.java index 62313b019..9daa1a03d 100755 --- a/src/test/java/org/springframework/data/repository/support/DomainClassConverterUnitTests.java +++ b/src/test/java/org/springframework/data/repository/support/DomainClassConverterUnitTests.java @@ -50,6 +50,7 @@ import org.springframework.web.bind.annotation.ModelAttribute; * * @author Oliver Gierke * @author Thomas Darimont + * @author Alessandro Nistico */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -85,7 +86,7 @@ class DomainClassConverterUnitTests { converter.setApplicationContext(initContextWithRepo()); - when(service.canConvert(String.class, Long.class)).thenReturn(true); + when(service.canConvert(STRING_TYPE, LONG_TYPE)).thenReturn(true); assertMatches(true); } @@ -116,7 +117,7 @@ class DomainClassConverterUnitTests { var context = initContextWithRepo(); converter.setApplicationContext(context); - doReturn(1L).when(service).convert(any(), eq(Long.class)); + doReturn(1L).when(service).convert(any(), eq(STRING_TYPE), eq(LONG_TYPE)); converter.convert("1", STRING_TYPE, USER_TYPE); @@ -133,7 +134,7 @@ class DomainClassConverterUnitTests { var context = new GenericApplicationContext(parent); context.refresh(); - when(service.canConvert(String.class, Long.class)).thenReturn(true); + when(service.canConvert(STRING_TYPE, LONG_TYPE)).thenReturn(true); converter.setApplicationContext(context); assertThat(converter.matches(STRING_TYPE, USER_TYPE)).isTrue(); @@ -169,7 +170,7 @@ class DomainClassConverterUnitTests { converter.setApplicationContext(initContextWithRepo()); - when(service.canConvert(Long.class, String.class)).thenReturn(true); + when(service.canConvert(LONG_TYPE, STRING_TYPE)).thenReturn(true); assertThat(converter.matches(USER_TYPE, STRING_TYPE)).isTrue(); } diff --git a/src/test/java/org/springframework/data/repository/support/RepositoriesUnitTests.java b/src/test/java/org/springframework/data/repository/support/RepositoriesUnitTests.java index a84495873..0ef34cf02 100755 --- a/src/test/java/org/springframework/data/repository/support/RepositoriesUnitTests.java +++ b/src/test/java/org/springframework/data/repository/support/RepositoriesUnitTests.java @@ -48,6 +48,8 @@ import org.springframework.data.repository.core.support.DummyRepositoryFactoryBe import org.springframework.data.repository.core.support.DummyRepositoryInformation; import org.springframework.data.repository.core.support.RepositoryFactoryInformation; import org.springframework.data.repository.query.QueryMethod; +import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; import org.springframework.util.ClassUtils; /** @@ -56,6 +58,7 @@ import org.springframework.util.ClassUtils; * @author Oliver Gierke * @author Thomas Darimont * @author Jan Zeppenfeld + * @author Alessandro Nistico */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -290,7 +293,7 @@ class RepositoriesUnitTests { static class CustomRepositoryMetadata extends DefaultRepositoryMetadata { - private final Class domainType; + private final TypeInformation domainType; /** * @param repositoryInterface @@ -302,14 +305,14 @@ class RepositoriesUnitTests { var domainType = super.getDomainType().getName().concat("Entity"); try { - this.domainType = ClassUtils.forName(domainType, CustomRepositoryMetadata.class.getClassLoader()); + this.domainType = ClassTypeInformation.from(ClassUtils.forName(domainType, CustomRepositoryMetadata.class.getClassLoader())); } catch (Exception e) { throw new RuntimeException(e); } } @Override - public Class getDomainType() { + public TypeInformation getDomainTypeInformation() { return this.domainType; }