From fc2135df3d94edbbe1c6c823ebe53e464d355721 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 31 May 2018 15:30:18 +0200 Subject: [PATCH] =?UTF-8?q?DATACMNS-1333=20-=20Unified=20is-new-detection?= =?UTF-8?q?=20in=20PersistentEntity.isNew(=E2=80=A6).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PersistentEntity now exposes an ….isNew(…) method that exposes the same detection algorithm previously exposed through MappingContextIsNewStrategyFactory (Persistable in favor of the version property in favor of an identifier lookup). MappingContextIsNewStrategyFactory has been refactored to return an ad-hoc strategy to delegate to the newly introduced method. The core message to implementing modules is that they should now prefer PersistentEntityInformation within their RepositoryFactorySupport implementation and move all customizations made in the store-specific EntityInformation implementation in PersistentEntity. --- .../auditing/IsNewAwareAuditingHandler.java | 18 +- .../data/mapping/PersistentEntity.java | 10 + .../MappingContextIsNewStrategyFactory.java | 60 +----- .../mapping/model/BasicPersistentEntity.java | 39 +++- .../model/PersistentEntityIsNewStrategy.java | 112 +++++++++++ .../repository/core/EntityInformation.java | 1 + .../support/PersistableEntityInformation.java | 2 + .../support/PersistentEntityInformation.java | 34 ++-- .../support/ReflectionEntityInformation.java | 2 + .../support/CachingIsNewStrategyFactory.java | 2 + .../data/support/IsNewStrategyFactory.java | 3 + .../support/IsNewStrategyFactorySupport.java | 1 + .../IsNewAwareAuditingHandlerUnitTests.java | 6 +- ...gContextIsNewStrategyFactoryUnitTests.java | 3 - ...ersistentEntityIsNewStrategyUnitTests.java | 174 ++++++++++++++++++ 15 files changed, 383 insertions(+), 84 deletions(-) create mode 100644 src/main/java/org/springframework/data/mapping/model/PersistentEntityIsNewStrategy.java create mode 100644 src/test/java/org/springframework/data/mapping/model/PersistentEntityIsNewStrategyUnitTests.java diff --git a/src/main/java/org/springframework/data/auditing/IsNewAwareAuditingHandler.java b/src/main/java/org/springframework/data/auditing/IsNewAwareAuditingHandler.java index db855b304..5f88635d4 100644 --- a/src/main/java/org/springframework/data/auditing/IsNewAwareAuditingHandler.java +++ b/src/main/java/org/springframework/data/auditing/IsNewAwareAuditingHandler.java @@ -20,14 +20,12 @@ import java.util.Optional; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory; import org.springframework.data.mapping.context.PersistentEntities; import org.springframework.data.support.IsNewStrategy; -import org.springframework.data.support.IsNewStrategyFactory; import org.springframework.util.Assert; /** - * {@link AuditingHandler} extension that uses an {@link IsNewStrategyFactory} to expose a generic + * {@link AuditingHandler} extension that uses {@link PersistentEntity#isNew(Object)} to expose a generic * {@link #markAudited(Optional)} method that will route calls to {@link #markCreated(Optional)} or * {@link #markModified(Optional)} based on the {@link IsNewStrategy} determined from the factory. * @@ -37,7 +35,7 @@ import org.springframework.util.Assert; */ public class IsNewAwareAuditingHandler extends AuditingHandler { - private final IsNewStrategyFactory isNewStrategyFactory; + private final PersistentEntities entities; /** * Creates a new {@link IsNewAwareAuditingHandler} for the given {@link MappingContext}. @@ -62,13 +60,12 @@ public class IsNewAwareAuditingHandler extends AuditingHandler { super(entities); - this.isNewStrategyFactory = new MappingContextIsNewStrategyFactory(entities); + this.entities = entities; } /** - * Marks the given object created or modified based on the {@link IsNewStrategy} returned by the - * {@link IsNewStrategyFactory} configured. Will rout the calls to {@link #markCreated(Optional)} and - * {@link #markModified(Optional)} accordingly. + * Marks the given object created or modified based on {@link PersistentEntity#isNew(Object)}. Will route the calls to + * {@link #markCreated(Optional)} and {@link #markModified(Optional)} accordingly. * * @param object */ @@ -80,9 +77,10 @@ public class IsNewAwareAuditingHandler extends AuditingHandler { return; } - IsNewStrategy strategy = isNewStrategyFactory.getIsNewStrategy(object.getClass()); + PersistentEntity> entity = entities + .getRequiredPersistentEntity(object.getClass()); - if (strategy.isNew(object)) { + if (entity.isNew(object)) { markCreated(object); } else { markModified(object); diff --git a/src/main/java/org/springframework/data/mapping/PersistentEntity.java b/src/main/java/org/springframework/data/mapping/PersistentEntity.java index 5b268fb8a..dcf4caeb9 100644 --- a/src/main/java/org/springframework/data/mapping/PersistentEntity.java +++ b/src/main/java/org/springframework/data/mapping/PersistentEntity.java @@ -295,4 +295,14 @@ public interface PersistentEntity> extends It * @since 1.10 */ IdentifierAccessor getIdentifierAccessor(Object bean); + + /** + * Returns whether the given bean is considered new according to the static metadata. + * + * @param bean must not be {@literal null}. + * @throws IllegalArgumentException in case the given bean is not an instance of the typ represented by the + * {@link PersistentEntity}. + * @return whether the given bean is considered a new instance. + */ + boolean isNew(Object bean); } diff --git a/src/main/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactory.java b/src/main/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactory.java index a83bda2a6..1b6f3ed08 100644 --- a/src/main/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactory.java +++ b/src/main/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactory.java @@ -15,13 +15,7 @@ */ package org.springframework.data.mapping.context; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -import java.util.function.Function; - import org.springframework.data.mapping.PersistentEntity; -import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.support.IsNewStrategy; import org.springframework.data.support.IsNewStrategyFactory; import org.springframework.data.support.IsNewStrategyFactorySupport; @@ -36,10 +30,13 @@ import org.springframework.util.Assert; * * @author Oliver Gierke * @author Mark Paluch + * @deprecated as of 2.1 in favor of looking up the {@link PersistentEntity} and calling + * {@link PersistentEntity#isNew(Object)} on it */ +@Deprecated public class MappingContextIsNewStrategyFactory extends IsNewStrategyFactorySupport { - private final PersistentEntities context; + private final PersistentEntities entities; /** * Creates a new {@link MappingContextIsNewStrategyFactory} using the given {@link MappingContext}. @@ -61,7 +58,8 @@ public class MappingContextIsNewStrategyFactory extends IsNewStrategyFactorySupp public MappingContextIsNewStrategyFactory(PersistentEntities entities) { Assert.notNull(entities, "PersistentEntities must not be null!"); - this.context = entities; + + this.entities = entities; } /* @@ -72,50 +70,8 @@ public class MappingContextIsNewStrategyFactory extends IsNewStrategyFactorySupp @Override protected IsNewStrategy doGetIsNewStrategy(Class type) { - PersistentEntity> entity = context.getRequiredPersistentEntity(type); + PersistentEntity entity = entities.getRequiredPersistentEntity(type); - if (entity.hasVersionProperty()) { - - return PersistentPropertyInspectingIsNewStrategy.of(entity.getRequiredVersionProperty(), - MappingContextIsNewStrategyFactory::propertyIsNullOrZeroNumber); - - } - - if (entity.hasIdProperty()) { - - return PersistentPropertyInspectingIsNewStrategy.of(entity.getRequiredIdProperty(), - MappingContextIsNewStrategyFactory::propertyIsNullOrZeroNumber); - } - - return null; - } - - /** - * {@link IsNewStrategy} implementation that will inspect a given {@link PersistentProperty} and call - * {@link #isNew(Object)} with the value retrieved by reflection. - * - * @author Oliver Gierke - */ - @RequiredArgsConstructor(staticName = "of") - static class PersistentPropertyInspectingIsNewStrategy implements IsNewStrategy { - - private final @NonNull PersistentProperty property; - private final @NonNull Function isNew; - - /* - * (non-Javadoc) - * @see org.springframework.data.support.IsNewStrategy#isNew(java.util.Optional) - */ - @Override - public boolean isNew(Object entity) { - - Assert.notNull(entity, "Entity must not be null!"); - - return isNew.apply(property.getOwner().getPropertyAccessor(entity).getProperty(property)); - } - } - - private static boolean propertyIsNullOrZeroNumber(Object value) { - return value == null || value instanceof Number && ((Number) value).longValue() == 0; + return bean -> entity.isNew(bean); } } diff --git a/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java b/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java index 26344f659..9c31bd68c 100644 --- a/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java +++ b/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java @@ -49,6 +49,8 @@ import org.springframework.data.mapping.SimpleAssociationHandler; import org.springframework.data.mapping.SimplePropertyHandler; import org.springframework.data.mapping.TargetAwareIdentifierAccessor; import org.springframework.data.spel.EvaluationContextProvider; +import org.springframework.data.support.IsNewStrategy; +import org.springframework.data.support.PersistableIsNewStrategy; import org.springframework.data.util.Lazy; import org.springframework.data.util.TypeInformation; import org.springframework.expression.EvaluationContext; @@ -90,6 +92,7 @@ public class BasicPersistentEntity> implement private EvaluationContextProvider evaluationContextProvider = EvaluationContextProvider.DEFAULT; private final Lazy typeAlias; + private final Lazy isNewStrategy; /** * Creates a new {@link BasicPersistentEntity} from the given {@link TypeInformation}. @@ -124,6 +127,10 @@ public class BasicPersistentEntity> implement this.propertyAnnotationCache = CollectionUtils.toMultiValueMap(new ConcurrentReferenceHashMap<>()); this.propertyAccessorFactory = BeanWrapperPropertyAccessorFactory.INSTANCE; this.typeAlias = Lazy.of(() -> getAliasFromAnnotation(getType())); + this.isNewStrategy = Lazy.of(() -> Persistable.class.isAssignableFrom(information.getType()) // + ? PersistableIsNewStrategy.INSTANCE + : PersistentEntityIsNewStrategy.of(this)); + } /* @@ -456,9 +463,7 @@ public class BasicPersistentEntity> implement @Override public PersistentPropertyAccessor getPropertyAccessor(Object bean) { - Assert.notNull(bean, "Target bean must not be null!"); - Assert.isTrue(getType().isInstance(bean), - () -> String.format(TYPE_MISMATCH, bean.getClass().getName(), getType().getName())); + verifyBeanType(bean); return propertyAccessorFactory.getPropertyAccessor(this, bean); } @@ -470,9 +475,7 @@ public class BasicPersistentEntity> implement @Override public IdentifierAccessor getIdentifierAccessor(Object bean) { - Assert.notNull(bean, "Target bean must not be null!"); - Assert.isTrue(getType().isInstance(bean), - () -> String.format(TYPE_MISMATCH, bean.getClass().getName(), getType().getName())); + verifyBeanType(bean); if (Persistable.class.isAssignableFrom(getType())) { return new PersistableIdentifierAccessor((Persistable) bean); @@ -481,6 +484,18 @@ public class BasicPersistentEntity> implement return hasIdProperty() ? new IdPropertyIdentifierAccessor(this, bean) : new AbsentIdentifierAccessor(bean); } + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentEntity#isNew(java.lang.Object) + */ + @Override + public boolean isNew(Object bean) { + + verifyBeanType(bean); + + return isNewStrategy.get().isNew(bean); + } + /* * (non-Javadoc) * @see java.lang.Iterable#iterator() @@ -494,6 +509,18 @@ public class BasicPersistentEntity> implement return evaluationContextProvider.getEvaluationContext(rootObject); } + /** + * Verifies the given bean type to no be {@literal null} and of the type of the current {@link PersistentEntity}. + * + * @param bean must not be {@literal null}. + */ + private final void verifyBeanType(Object bean) { + + Assert.notNull(bean, "Target bean must not be null!"); + Assert.isInstanceOf(getType(), bean, + () -> String.format(TYPE_MISMATCH, bean.getClass().getName(), getType().getName())); + } + /** * Calculates the {@link Alias} to be used for the given type. * diff --git a/src/main/java/org/springframework/data/mapping/model/PersistentEntityIsNewStrategy.java b/src/main/java/org/springframework/data/mapping/model/PersistentEntityIsNewStrategy.java new file mode 100644 index 000000000..fd696e1c5 --- /dev/null +++ b/src/main/java/org/springframework/data/mapping/model/PersistentEntityIsNewStrategy.java @@ -0,0 +1,112 @@ +/* + * Copyright 2018 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 + * + * http://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 java.util.function.Function; + +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.support.IsNewStrategy; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +/** + * An {@link IsNewStrategy} to use a {@link PersistentEntity}'s version property followed by it + * + * @author Oliver Gierke + * @soundtrack Scary Pockets - Crash Into Me (Dave Matthews Band Cover feat. Julia Nunes) - + * https://www.youtube.com/watch?v=syGlBNVGEqU + */ +class PersistentEntityIsNewStrategy implements IsNewStrategy { + + private final Function valueLookup; + private final @Nullable Class valueType; + + /** + * Creates a new {@link PersistentEntityIsNewStrategy} for the given entity. + * + * @param entity must not be {@literal null}. + */ + private PersistentEntityIsNewStrategy(PersistentEntity entity, boolean idOnly) { + + Assert.notNull(entity, "PersistentEntity must not be null!"); + + this.valueLookup = entity.hasVersionProperty() && !idOnly // + ? source -> entity.getPropertyAccessor(source).getProperty(entity.getRequiredVersionProperty()) + : source -> entity.getIdentifierAccessor(source).getIdentifier(); + + this.valueType = entity.hasVersionProperty() && !idOnly // + ? entity.getRequiredVersionProperty().getType() // + : entity.hasIdProperty() ? entity.getRequiredIdProperty().getType() : null; + + Class type = valueType; + + if (type != null && type.isPrimitive()) { + + if (!ClassUtils.isAssignable(Number.class, type)) { + + throw new IllegalArgumentException(String + .format("Only numeric primitives are supported as identifier / version field types! Got: %s.", valueType)); + } + } + } + + /** + * Creates a new {@link PersistentEntityIsNewStrategy} to only consider the identifier of the given entity. + * + * @param entity must not be {@literal null}. + * @return + */ + public static PersistentEntityIsNewStrategy forIdOnly(PersistentEntity entity) { + return new PersistentEntityIsNewStrategy(entity, true); + } + + /** + * Creates a new {@link PersistentEntityIsNewStrategy} to consider version properties before falling back to the + * identifier. + * + * @param entity must not be {@literal null}. + * @return + */ + public static PersistentEntityIsNewStrategy of(PersistentEntity entity) { + return new PersistentEntityIsNewStrategy(entity, false); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.support.IsNewStrategy#isNew(java.lang.Object) + */ + @Override + public boolean isNew(Object entity) { + + Object value = valueLookup.apply(entity); + + if (value == null) { + return true; + } + + if (valueType != null && !valueType.isPrimitive()) { + return value == null; + } + + if (Number.class.isInstance(value)) { + return ((Number) value).longValue() == 0; + } + + throw new IllegalArgumentException( + String.format("Could not determine whether %s is new! Unsupported identifier or version property!", entity)); + } +} diff --git a/src/main/java/org/springframework/data/repository/core/EntityInformation.java b/src/main/java/org/springframework/data/repository/core/EntityInformation.java index f3fe5ba82..ee7b15752 100644 --- a/src/main/java/org/springframework/data/repository/core/EntityInformation.java +++ b/src/main/java/org/springframework/data/repository/core/EntityInformation.java @@ -23,6 +23,7 @@ import org.springframework.util.Assert; * * @author Oliver Gierke * @author Mark Paluch + * @see org.springframework.data.repository.core.support.PersistentEntityInformation */ public interface EntityInformation extends EntityMetadata { diff --git a/src/main/java/org/springframework/data/repository/core/support/PersistableEntityInformation.java b/src/main/java/org/springframework/data/repository/core/support/PersistableEntityInformation.java index d72eaf8ee..a0b6c575b 100644 --- a/src/main/java/org/springframework/data/repository/core/support/PersistableEntityInformation.java +++ b/src/main/java/org/springframework/data/repository/core/support/PersistableEntityInformation.java @@ -25,7 +25,9 @@ import org.springframework.lang.Nullable; * {@link Persistable#isNew()} for the {@link #isNew(Object)} check. * * @author Oliver Gierke + * @deprecated as of 2.1 in favor of {@link PersistentEntityInformation}. */ +@Deprecated public class PersistableEntityInformation, ID> extends AbstractEntityInformation { private Class idClass; diff --git a/src/main/java/org/springframework/data/repository/core/support/PersistentEntityInformation.java b/src/main/java/org/springframework/data/repository/core/support/PersistentEntityInformation.java index 1611aba34..d5e18f1be 100644 --- a/src/main/java/org/springframework/data/repository/core/support/PersistentEntityInformation.java +++ b/src/main/java/org/springframework/data/repository/core/support/PersistentEntityInformation.java @@ -15,6 +15,9 @@ */ package org.springframework.data.repository.core.support; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.repository.core.EntityInformation; @@ -27,20 +30,18 @@ import org.springframework.lang.Nullable; * @author Oliver Gierke * @author Christoph Strobl */ -@SuppressWarnings("unchecked") -public class PersistentEntityInformation extends AbstractEntityInformation { +@RequiredArgsConstructor +public class PersistentEntityInformation implements EntityInformation { - private final PersistentEntity> persistentEntity; + private final @NonNull PersistentEntity> persistentEntity; - /** - * Creates a new {@link PersistableEntityInformation} for the given {@link PersistentEntity}. - * - * @param entity must not be {@literal null}. + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.support.AbstractEntityInformation#isNew(java.lang.Object) */ - public PersistentEntityInformation(PersistentEntity entity) { - - super(entity.getType()); - this.persistentEntity = entity; + @Override + public boolean isNew(T entity) { + return persistentEntity.isNew(entity); } /* @@ -49,15 +50,26 @@ public class PersistentEntityInformation extends AbstractEntityInformatio */ @Nullable @Override + @SuppressWarnings("unchecked") public ID getId(T entity) { return (ID) persistentEntity.getIdentifierAccessor(entity).getIdentifier(); } + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.EntityMetadata#getJavaType() + */ + @Override + public Class getJavaType() { + return persistentEntity.getType(); + } + /* * (non-Javadoc) * @see org.springframework.data.repository.core.EntityInformation#getIdType() */ @Override + @SuppressWarnings("unchecked") public Class getIdType() { return (Class) persistentEntity.getRequiredIdProperty().getType(); } diff --git a/src/main/java/org/springframework/data/repository/core/support/ReflectionEntityInformation.java b/src/main/java/org/springframework/data/repository/core/support/ReflectionEntityInformation.java index 23eb3f55f..6e048cda8 100644 --- a/src/main/java/org/springframework/data/repository/core/support/ReflectionEntityInformation.java +++ b/src/main/java/org/springframework/data/repository/core/support/ReflectionEntityInformation.java @@ -31,7 +31,9 @@ import org.springframework.util.ReflectionUtils; * * @author Oliver Gierke * @author Christoph Strobl + * @deprecated since 2.1 in favor of {@link PersistentEntityInformation} */ +@Deprecated public class ReflectionEntityInformation extends AbstractEntityInformation { private static final Class DEFAULT_ID_ANNOTATION = Id.class; diff --git a/src/main/java/org/springframework/data/support/CachingIsNewStrategyFactory.java b/src/main/java/org/springframework/data/support/CachingIsNewStrategyFactory.java index 608f9b1d5..0a9312f1f 100644 --- a/src/main/java/org/springframework/data/support/CachingIsNewStrategyFactory.java +++ b/src/main/java/org/springframework/data/support/CachingIsNewStrategyFactory.java @@ -27,7 +27,9 @@ import java.util.concurrent.ConcurrentHashMap; * * @author Oliver Gierke * @author Christoph Strobl + * @deprecated as of 2.1 in favor of {@link org.springframework.data.mapping.PersistentEntity#isNew(Object)} */ +@Deprecated @RequiredArgsConstructor public class CachingIsNewStrategyFactory implements IsNewStrategyFactory { diff --git a/src/main/java/org/springframework/data/support/IsNewStrategyFactory.java b/src/main/java/org/springframework/data/support/IsNewStrategyFactory.java index 5a1a0b096..efccd54dc 100644 --- a/src/main/java/org/springframework/data/support/IsNewStrategyFactory.java +++ b/src/main/java/org/springframework/data/support/IsNewStrategyFactory.java @@ -20,7 +20,10 @@ package org.springframework.data.support; * * @author Oliver Gierke * @since 1.5 + * @deprecated since 2.1 in favor of simply calling + * {@link org.springframework.data.mapping.PersistentEntity#isNew(Object)} */ +@Deprecated public interface IsNewStrategyFactory { /** diff --git a/src/main/java/org/springframework/data/support/IsNewStrategyFactorySupport.java b/src/main/java/org/springframework/data/support/IsNewStrategyFactorySupport.java index f680902f7..62eac064f 100644 --- a/src/main/java/org/springframework/data/support/IsNewStrategyFactorySupport.java +++ b/src/main/java/org/springframework/data/support/IsNewStrategyFactorySupport.java @@ -25,6 +25,7 @@ import org.springframework.util.Assert; * * @author Oliver Gierke * @since 1.5 + * @deprecated since 2.1 in favor of {@link org.springframework.data.mapping.PersistentEntity#isNew(Object)} */ public abstract class IsNewStrategyFactorySupport implements IsNewStrategyFactory { diff --git a/src/test/java/org/springframework/data/auditing/IsNewAwareAuditingHandlerUnitTests.java b/src/test/java/org/springframework/data/auditing/IsNewAwareAuditingHandlerUnitTests.java index acbb35d13..25ce5afc0 100755 --- a/src/test/java/org/springframework/data/auditing/IsNewAwareAuditingHandlerUnitTests.java +++ b/src/test/java/org/springframework/data/auditing/IsNewAwareAuditingHandlerUnitTests.java @@ -16,7 +16,6 @@ package org.springframework.data.auditing; import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; import java.util.Optional; @@ -41,12 +40,15 @@ public class IsNewAwareAuditingHandlerUnitTests extends AuditingHandlerUnitTests @Before public void init() { + this.mappingContext = new SampleMappingContext(); + this.mappingContext.getPersistentEntity(AuditedUser.class); + this.mappingContext.afterPropertiesSet(); } @Override protected IsNewAwareAuditingHandler getHandler() { - return new IsNewAwareAuditingHandler(mock(PersistentEntities.class)); + return new IsNewAwareAuditingHandler(PersistentEntities.of(mappingContext)); } @Test diff --git a/src/test/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactoryUnitTests.java b/src/test/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactoryUnitTests.java index 5016fd26a..73458e6df 100755 --- a/src/test/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactoryUnitTests.java @@ -59,9 +59,6 @@ public class MappingContextIsNewStrategyFactoryUnitTests { entity.id = 1L; assertThat(strategy.isNew(entity)).isTrue(); - entity.version = 0L; - assertThat(strategy.isNew(entity)).isTrue(); - entity.version = 1L; assertThat(strategy.isNew(entity)).isFalse(); } diff --git a/src/test/java/org/springframework/data/mapping/model/PersistentEntityIsNewStrategyUnitTests.java b/src/test/java/org/springframework/data/mapping/model/PersistentEntityIsNewStrategyUnitTests.java new file mode 100644 index 000000000..6dbdb750e --- /dev/null +++ b/src/test/java/org/springframework/data/mapping/model/PersistentEntityIsNewStrategyUnitTests.java @@ -0,0 +1,174 @@ +/* + * Copyright 2018 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 + * + * http://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 lombok.AllArgsConstructor; + +import org.junit.Test; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Version; +import org.springframework.data.domain.Persistable; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.context.SampleMappingContext; + +/** + * Unit tests for {@link PersistentEntityIsNewStrategy}. + * + * @author Oliver Gierke + * @soundtrack Scary Pockets - Crash Into Me (Dave Matthews Band Cover feat. Julia Nunes) - + * https://www.youtube.com/watch?v=syGlBNVGEqU + */ +public class PersistentEntityIsNewStrategyUnitTests { + + SampleMappingContext context = new SampleMappingContext(); + + @Test // DATACMNS-133 + public void detectsNewEntityForPrimitiveId() { + + PersistentEntity entity = context.getRequiredPersistentEntity(PrimitiveIdEntity.class); + + PrimitiveIdEntity bean = new PrimitiveIdEntity(); + assertThat(entity.isNew(bean)).isTrue(); + } + + @Test // DATACMNS-133 + public void detectsNotNewEntityForPrimitiveId() { + + PersistentEntity entity = context.getRequiredPersistentEntity(PrimitiveIdEntity.class); + + PrimitiveIdEntity bean = new PrimitiveIdEntity(); + + bean.id = 1L; + assertThat(entity.isNew(bean)).isFalse(); + } + + @Test // DATACMNS-133 + public void detectsNewEntityForWrapperId() { + + PersistentEntity entity = context.getRequiredPersistentEntity(PrimitiveWrapperIdEntity.class); + + PrimitiveWrapperIdEntity bean = new PrimitiveWrapperIdEntity(); + assertThat(entity.isNew(bean)).isTrue(); + } + + @Test // DATACMNS-133 + public void detectsNotNewEntityForWrapperId() { + + PersistentEntity entity = context.getRequiredPersistentEntity(PrimitiveWrapperIdEntity.class); + + PrimitiveWrapperIdEntity bean = new PrimitiveWrapperIdEntity(); + + bean.id = 0L; + assertThat(entity.isNew(bean)).isFalse(); + } + + @Test // DATACMNS-133 + public void rejectsUnsupportedIdentifierType() { + + PersistentEntity entity = context.getRequiredPersistentEntity(UnsupportedPrimitiveIdEntity.class); + + assertThatIllegalArgumentException() // + .isThrownBy(() -> PersistentEntityIsNewStrategy.of(entity)) // + .withMessageContaining(boolean.class.getSimpleName()); + } + + @Test // DATACMNS-1333 + public void discoversNewPersistableEntity() { + + PersistentEntity entity = context.getRequiredPersistentEntity(PersistableEntity.class); + + assertThat(entity.isNew(new PersistableEntity(false))).isFalse(); + } + + @Test // DATACMNS-1333 + public void discoversNonNewPersistableEntity() { + + PersistentEntity entity = context.getRequiredPersistentEntity(PersistableEntity.class); + + assertThat(entity.isNew(new PersistableEntity(true))).isTrue(); + } + + @Test // DATACMNS-1333 + public void prefersVersionOverIdentifier() { + + PersistentEntity entity = context.getRequiredPersistentEntity(VersionedEntity.class); + + VersionedEntity bean = new VersionedEntity(); + + bean.id = 1L; + assertThat(entity.isNew(bean)).isTrue(); + + bean.version = 1L; + assertThat(entity.isNew(bean)).isFalse(); + } + + @Test // DATACMNS-1333 + public void considersEntityWithoutIdNew() { + + PersistentEntity entity = context.getRequiredPersistentEntity(NoIdEntity.class); + + assertThat(entity.isNew(new NoIdEntity())).isTrue(); + } + + static class PrimitiveIdEntity { + + @Id long id; + } + + static class PrimitiveWrapperIdEntity { + + @Id Long id; + } + + static class UnsupportedPrimitiveIdEntity { + + @Id boolean id; + } + + static class VersionedEntity { + + @Version Long version; + @Id Long id; + } + + @AllArgsConstructor + static class PersistableEntity implements Persistable { + + boolean isNew; + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Persistable#isNew() + */ + @Override + public boolean isNew() { + return isNew; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.domain.Persistable#getId() + */ + @Override + public Long getId() { + return null; + } + } + + static class NoIdEntity {} +}