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:
@@ -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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user