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 {} +}