Ignore override properties when creating PersistentEntity.

Original pull request: #390.
Closes #1911.
This commit is contained in:
Christoph Strobl
2019-04-05 18:34:40 +02:00
committed by Mark Paluch
parent 35b8a42e3d
commit 85578b5cb1
3 changed files with 142 additions and 0 deletions

View File

@@ -30,6 +30,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
@@ -62,6 +64,7 @@ import org.springframework.data.util.Streamable;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.ReflectionUtils.FieldFilter;
@@ -87,6 +90,8 @@ import org.springframework.util.ReflectionUtils.FieldFilter;
public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?, P>, P extends PersistentProperty<P>>
implements MappingContext<E, P>, ApplicationEventPublisherAware, ApplicationContextAware, InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(MappingContext.class);
private final Optional<E> NONE = Optional.empty();
private final Map<TypeInformation<?>, Optional<E>> persistentEntities = new HashMap<>();
private final PersistentPropertyAccessorFactory persistentPropertyAccessorFactory;
@@ -550,6 +555,10 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
return;
}
if (isKotlinOverride(property, input)) {
return;
}
entity.addPersistentProperty(property);
if (property.isAssociation()) {
@@ -562,6 +571,38 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
property.getPersistentEntityTypes().forEach(AbstractMappingContext.this::addPersistentEntity);
}
private boolean isKotlinOverride(P property, Property input) {
if (!KotlinDetector.isKotlinPresent() || !input.getField().isPresent()) {
return false;
}
Field field = input.getField().get();
if (!KotlinDetector.isKotlinType(field.getDeclaringClass())) {
return false;
}
for (P existingProperty : entity) {
if (!property.getName().equals(existingProperty.getName())) {
continue;
}
if (field.getDeclaringClass() != entity.getType()
&& ClassUtils.isAssignable(field.getDeclaringClass(), entity.getType())) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(String.format("Skipping '%s.%s' property declaration shadowed by '%s %s' in '%s'. ",
field.getDeclaringClass().getName(), property.getName(), property.getType().getSimpleName(),
property.getName(), entity.getType().getSimpleName()));
}
return true;
}
}
return false;
}
}
/**

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2019 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
*
* https://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
open class ShadowedPropertyType {
open val shadowedProperty: Int = 1
}
class ShadowingPropertyType : ShadowedPropertyType() {
override var shadowedProperty: Int = 10
}
open class ShadowedPropertyTypeWithCtor(open val shadowedProperty: Int)
class ShadowingPropertyTypeWithCtor(val someValue: String, override var shadowedProperty: Int = 1) : ShadowedPropertyTypeWithCtor(shadowedProperty)

View File

@@ -41,9 +41,15 @@ import org.springframework.context.ApplicationEvent;
import org.springframework.data.annotation.Id;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.ShadowedPropertyType;
import org.springframework.data.mapping.ShadowedPropertyTypeWithCtor;
import org.springframework.data.mapping.ShadowingPropertyType;
import org.springframework.data.mapping.ShadowingPropertyTypeWithCtor;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.StreamUtils;
import org.springframework.data.util.TypeInformation;
/**
@@ -52,6 +58,7 @@ import org.springframework.data.util.TypeInformation;
* @author Oliver Gierke
* @author Thomas Darimont
* @author Mark Paluch
* @author Christoph Stobl
*/
class AbstractMappingContextUnitTests {
@@ -216,6 +223,37 @@ class AbstractMappingContextUnitTests {
.isThrownBy(() -> context.getPersistentEntity(Unsupported.class));
}
@Test // DATACMNS-1509
public void shouldIgnoreKotlinOverrideCtorPropertyInSuperClass() {
BasicPersistentEntity<Object, SamplePersistentProperty> entity = context
.getPersistentEntity(ClassTypeInformation.from(ShadowingPropertyTypeWithCtor.class));
entity.doWithProperties((PropertyHandler<SamplePersistentProperty>) property -> {
assertThat(property.getField().getDeclaringClass()).isNotEqualTo(ShadowedPropertyTypeWithCtor.class);
});
}
@Test // DATACMNS-1509
public void shouldIgnoreKotlinOverridePropertyInSuperClass() {
BasicPersistentEntity<Object, SamplePersistentProperty> entity = context
.getPersistentEntity(ClassTypeInformation.from(ShadowingPropertyType.class));
entity.doWithProperties((PropertyHandler<SamplePersistentProperty>) property -> {
assertThat(property.getField().getDeclaringClass()).isNotEqualTo(ShadowedPropertyType.class);
});
}
@Test // DATACMNS-1509
public void shouldStillIncludeNonKotlinShadowedPropertyInSuperClass() {
BasicPersistentEntity<Object, SamplePersistentProperty> entity = context
.getPersistentEntity(ClassTypeInformation.from(ShadowingProperty.class));
assertThat(StreamUtils.createStreamFromIterator(entity.iterator())
.filter(it -> it.getField().getDeclaringClass().equals(ShadowedProperty.class)).findFirst() //
).isNotEmpty();
}
private static void assertHasEntityFor(Class<?> type, SampleMappingContext context, boolean expected) {
boolean found = false;
@@ -306,4 +344,37 @@ class AbstractMappingContextUnitTests {
};
}
}
static class ShadowedProperty {
private final String value;
ShadowedProperty(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
static class ShadowingProperty extends ShadowedProperty {
private String value;
ShadowingProperty(String value) {
super(value);
this.value = value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String getValue() {
return value;
}
}
}