diff --git a/src/main/java/org/springframework/data/mapping/context/PersistentEntities.java b/src/main/java/org/springframework/data/mapping/context/PersistentEntities.java index 56cfed394..9ad53e850 100644 --- a/src/main/java/org/springframework/data/mapping/context/PersistentEntities.java +++ b/src/main/java/org/springframework/data/mapping/context/PersistentEntities.java @@ -15,12 +15,17 @@ */ package org.springframework.data.mapping.context; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.function.BiFunction; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; @@ -33,11 +38,12 @@ import org.springframework.util.Assert; * Value object to access {@link PersistentEntity} instances managed by {@link MappingContext}s. * * @author Oliver Gierke + * @author Christoph Strobl * @since 1.8 */ public class PersistentEntities implements Streamable>> { - private final Streamable>> contexts; + private final Collection>> contexts; /** * Creates a new {@link PersistentEntities} for the given {@link MappingContext}s. @@ -48,7 +54,9 @@ public class PersistentEntities implements Streamable>>) contexts + : StreamSupport.stream(contexts.spliterator(), false).collect(Collectors.toList()); } /** @@ -74,26 +82,46 @@ public class PersistentEntities implements Streamable>> getPersistentEntity(Class type) { - return contexts.stream()// - .filter(it -> it.hasPersistentEntityFor(type))// - .findFirst().map(it -> it.getRequiredPersistentEntity(type)); + for (MappingContext> context : contexts) { + if (context.hasPersistentEntityFor(type)) { + return Optional.of(context.getRequiredPersistentEntity(type)); + } + } + + return Optional.empty(); } /** - * Returns the {@link PersistentEntity} for the given type. Will consider all {@link MappingContext}s registered but - * throw an {@link IllegalArgumentException} in case none of the registered ones already have a - * {@link PersistentEntity} registered for the given type. + * Returns the {@link PersistentEntity} for the given type. Will consider all {@link MappingContext}s registered and + * create a new {@link PersistentEntity} in case none of the registered ones already have it registered for the given + * type, if there is only one context available. * * @param type must not be {@literal null}. * @return the {@link PersistentEntity} for the given domain type. - * @throws IllegalArgumentException in case no {@link PersistentEntity} can be found for the given type. + * @throws IllegalArgumentException in case no {@link PersistentEntity} can be found/created for the given type. + * @throws org.springframework.data.mapping.MappingException if the {@link PersistentEntity} cannot be + * {@link MappingContext#getPersistentEntity(Class) created} by the underlying {@link MappingContext}. */ public PersistentEntity> getRequiredPersistentEntity(Class type) { Assert.notNull(type, "Domain type must not be null!"); - return getPersistentEntity(type).orElseThrow( - () -> new IllegalArgumentException(String.format("Couldn't find PersistentEntity for type %s!", type))); + return getPersistentEntity(type).orElseGet(() -> { + + if (contexts.size() != 1) { + + throw new IllegalArgumentException(String.format( + "Couldn't create PersistentEntity for type %s! PersistentEntities knows about %s MappingContext instances and therefore cannot tell which is the responsible one. Please set the base package in your configuration to pre initialize contexts.", + type, contexts.size())); + } + + PersistentEntity> entity = contexts.iterator().next() + .getPersistentEntity(type); + if (entity == null) { + throw new IllegalArgumentException(String.format("Couldn't find PersistentEntity for type %s!", type)); + } + return entity; + }); } /** @@ -123,9 +151,11 @@ public class PersistentEntities implements Streamable> getManagedTypes() { - return Streamable.of(contexts.stream()// - .flatMap(it -> it.getManagedTypes().stream())// - .collect(Collectors.toSet())); + Set> target = new HashSet<>(); + for (MappingContext> context : contexts) { + target.addAll(context.getManagedTypes()); + } + return Streamable.of(target); } /* @@ -135,9 +165,11 @@ public class PersistentEntities implements Streamable>> iterator() { - return contexts.stream() - .>> flatMap(it -> it.getPersistentEntities().stream()) - .collect(Collectors.toList()).iterator(); + List>> target = new ArrayList<>(); + for (MappingContext> context : contexts) { + target.addAll(context.getPersistentEntities()); + } + return target.iterator(); } /** diff --git a/src/test/java/org/springframework/data/auditing/IsNewAwareAuditingHandlerUnitTests.java b/src/test/java/org/springframework/data/auditing/IsNewAwareAuditingHandlerUnitTests.java index f2b2209cb..1c1083d1c 100755 --- a/src/test/java/org/springframework/data/auditing/IsNewAwareAuditingHandlerUnitTests.java +++ b/src/test/java/org/springframework/data/auditing/IsNewAwareAuditingHandlerUnitTests.java @@ -16,6 +16,7 @@ package org.springframework.data.auditing; import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; import java.util.Optional; @@ -103,6 +104,19 @@ class IsNewAwareAuditingHandlerUnitTests extends AuditingHandlerUnitTests { getHandler().markAudited(Optional.of(new EntityWithoutId())); } + @Test // DATACMNS-1780 + void singleContextAllowsInFlightMetadataCreationForUnknownPersistentEntities() { + + SampleMappingContext mappingContext = spy(new SampleMappingContext()); + mappingContext.afterPropertiesSet(); + + AuditedUser user = new AuditedUser(); + user.id = 1L; + + new IsNewAwareAuditingHandler(PersistentEntities.of(mappingContext)).markAudited(user); + verify(mappingContext).getPersistentEntity(AuditedUser.class); + } + static class Domain { @Id Long id; diff --git a/src/test/java/org/springframework/data/mapping/context/PersistentEntitiesUnitTests.java b/src/test/java/org/springframework/data/mapping/context/PersistentEntitiesUnitTests.java index 83e5b587a..9b093965c 100755 --- a/src/test/java/org/springframework/data/mapping/context/PersistentEntitiesUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/context/PersistentEntitiesUnitTests.java @@ -28,12 +28,14 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Reference; import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.util.ClassTypeInformation; /** * Unit tests for {@link PersistentEntities}. * * @author Oliver Gierke + * @author Christoph Strobl */ @ExtendWith(MockitoExtension.class) class PersistentEntitiesUnitTests { @@ -51,6 +53,7 @@ class PersistentEntitiesUnitTests { when(first.hasPersistentEntityFor(Sample.class)).thenReturn(false); when(second.hasPersistentEntityFor(Sample.class)).thenReturn(true); + when(second.getRequiredPersistentEntity(Sample.class)).thenReturn(mock(BasicPersistentEntity.class)); PersistentEntities.of(first, second).getPersistentEntity(Sample.class); @@ -143,6 +146,21 @@ class PersistentEntitiesUnitTests { assertThat(entity.getType()).isEqualTo(SecondWithGenericId.class); } + @Test // DATACMNS-1780 + void getRequiredPersistentEntityAllowsInFlightEntityCreationForUnknownTypesWhenHavingJustASingleMappingContext() { + + SampleMappingContext mappingContext = spy(new SampleMappingContext()); + mappingContext.afterPropertiesSet(); + + assertThat(PersistentEntities.of(mappingContext).getRequiredPersistentEntity(Sample.class)).isNotNull(); + } + + @Test // DATACMNS-1780 + void getRequiredPersistentEntityErrorsOnInFlightEntityCreationForUnknownTypesWhenHavingMultipleMappingContexts() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> PersistentEntities.of(first, second).getRequiredPersistentEntity(Sample.class)); + } + static class Sample { @Id String id; }