DATACMNS-1333 - Unified is-new-detection in PersistentEntity.isNew(…).
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.
This commit is contained in:
@@ -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<?, ? extends PersistentProperty<?>> entity = entities
|
||||
.getRequiredPersistentEntity(object.getClass());
|
||||
|
||||
if (strategy.isNew(object)) {
|
||||
if (entity.isNew(object)) {
|
||||
markCreated(object);
|
||||
} else {
|
||||
markModified(object);
|
||||
|
||||
@@ -295,4 +295,14 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> 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);
|
||||
}
|
||||
|
||||
@@ -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<?, ? extends PersistentProperty<?>> 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<Object, Boolean> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T, P extends PersistentProperty<P>> implement
|
||||
private EvaluationContextProvider evaluationContextProvider = EvaluationContextProvider.DEFAULT;
|
||||
|
||||
private final Lazy<Alias> typeAlias;
|
||||
private final Lazy<IsNewStrategy> isNewStrategy;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BasicPersistentEntity} from the given {@link TypeInformation}.
|
||||
@@ -124,6 +127,10 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> 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<T, P extends PersistentProperty<P>> 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<T, P extends PersistentProperty<P>> 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<T, P extends PersistentProperty<P>> 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<T, P extends PersistentProperty<P>> 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.
|
||||
*
|
||||
|
||||
@@ -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<Object, Object> 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));
|
||||
}
|
||||
}
|
||||
@@ -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<T, ID> extends EntityMetadata<T> {
|
||||
|
||||
|
||||
@@ -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<T extends Persistable<ID>, ID> extends AbstractEntityInformation<T, ID> {
|
||||
|
||||
private Class<ID> idClass;
|
||||
|
||||
@@ -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<T, ID> extends AbstractEntityInformation<T, ID> {
|
||||
@RequiredArgsConstructor
|
||||
public class PersistentEntityInformation<T, ID> implements EntityInformation<T, ID> {
|
||||
|
||||
private final PersistentEntity<T, ? extends PersistentProperty<?>> persistentEntity;
|
||||
private final @NonNull PersistentEntity<T, ? extends PersistentProperty<?>> 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<T, ?> entity) {
|
||||
|
||||
super(entity.getType());
|
||||
this.persistentEntity = entity;
|
||||
@Override
|
||||
public boolean isNew(T entity) {
|
||||
return persistentEntity.isNew(entity);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -49,15 +50,26 @@ public class PersistentEntityInformation<T, ID> 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<T> getJavaType() {
|
||||
return persistentEntity.getType();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.EntityInformation#getIdType()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<ID> getIdType() {
|
||||
return (Class<ID>) persistentEntity.getRequiredIdProperty().getType();
|
||||
}
|
||||
|
||||
@@ -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<T, ID> extends AbstractEntityInformation<T, ID> {
|
||||
|
||||
private static final Class<Id> DEFAULT_ID_ANNOTATION = Id.class;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user