Skip to content

Commit 766bc65

Browse files
DATACMNS-1509 - Ignore Kotlin override properties when creating PersistentEntity.
1 parent 73581f4 commit 766bc65

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java

+42
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,16 @@
3434
import java.util.function.Predicate;
3535
import java.util.stream.Collectors;
3636

37+
import org.slf4j.Logger;
38+
import org.slf4j.LoggerFactory;
3739
import org.springframework.beans.BeanUtils;
3840
import org.springframework.beans.BeansException;
3941
import org.springframework.beans.factory.InitializingBean;
4042
import org.springframework.context.ApplicationContext;
4143
import org.springframework.context.ApplicationContextAware;
4244
import org.springframework.context.ApplicationEventPublisher;
4345
import org.springframework.context.ApplicationEventPublisherAware;
46+
import org.springframework.core.KotlinDetector;
4447
import org.springframework.data.mapping.MappingException;
4548
import org.springframework.data.mapping.PersistentEntity;
4649
import org.springframework.data.mapping.PersistentProperty;
@@ -60,6 +63,7 @@
6063
import org.springframework.data.util.TypeInformation;
6164
import org.springframework.lang.Nullable;
6265
import org.springframework.util.Assert;
66+
import org.springframework.util.ClassUtils;
6367
import org.springframework.util.ReflectionUtils;
6468
import org.springframework.util.ReflectionUtils.FieldCallback;
6569
import org.springframework.util.ReflectionUtils.FieldFilter;
@@ -85,6 +89,8 @@
8589
public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?, P>, P extends PersistentProperty<P>>
8690
implements MappingContext<E, P>, ApplicationEventPublisherAware, ApplicationContextAware, InitializingBean {
8791

92+
private static final Logger LOGGER = LoggerFactory.getLogger(MappingContext.class);
93+
8894
private final Optional<E> NONE = Optional.empty();
8995
private final Map<TypeInformation<?>, Optional<E>> persistentEntities = new HashMap<>();
9096
private final PersistentPropertyAccessorFactory persistentPropertyAccessorFactory = new ClassGeneratingPropertyAccessorFactory();
@@ -535,6 +541,10 @@ private void createAndRegisterProperty(Property input) {
535541
return;
536542
}
537543

544+
if (isKotlinOverride(property, input)) {
545+
return;
546+
}
547+
538548
entity.addPersistentProperty(property);
539549

540550
if (property.isAssociation()) {
@@ -547,6 +557,38 @@ private void createAndRegisterProperty(Property input) {
547557

548558
property.getPersistentEntityTypes().forEach(AbstractMappingContext.this::addPersistentEntity);
549559
}
560+
561+
private boolean isKotlinOverride(P property, Property input) {
562+
563+
if (!KotlinDetector.isKotlinPresent() || !input.getField().isPresent()) {
564+
return false;
565+
}
566+
567+
Field field = input.getField().get();
568+
if (!KotlinDetector.isKotlinType(field.getDeclaringClass())) {
569+
return false;
570+
}
571+
572+
for (P existingProperty : entity) {
573+
574+
if (!property.getName().equals(existingProperty.getName())) {
575+
continue;
576+
}
577+
578+
if (field.getDeclaringClass() != entity.getType()
579+
&& ClassUtils.isAssignable(field.getDeclaringClass(), entity.getType())) {
580+
581+
if (LOGGER.isTraceEnabled()) {
582+
LOGGER.trace(String.format("Skipping '%s.%s' property declaration shadowed by '%s %s' in '%s'. ",
583+
field.getDeclaringClass().getName(), property.getName(), property.getType().getSimpleName(),
584+
property.getName(), entity.getType().getSimpleName()));
585+
}
586+
return true;
587+
}
588+
}
589+
590+
return false;
591+
}
550592
}
551593

552594
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mapping
17+
18+
open class ShadowedPropertyType {
19+
open val shadowedProperty: Int = 1
20+
}
21+
22+
class ShadowingPropertyType : ShadowedPropertyType() {
23+
override var shadowedProperty: Int = 10
24+
}
25+
26+
open class ShadowedPropertyTypeWithCtor(open val shadowedProperty: Int)
27+
28+
class ShadowingPropertyTypeWithCtor(val someValue: String, override var shadowedProperty: Int = 1) : ShadowedPropertyTypeWithCtor(shadowedProperty)
29+
30+

src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java

+71
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,15 @@
3434
import org.springframework.data.annotation.Id;
3535
import org.springframework.data.mapping.MappingException;
3636
import org.springframework.data.mapping.PersistentEntity;
37+
import org.springframework.data.mapping.PropertyHandler;
38+
import org.springframework.data.mapping.ShadowedPropertyType;
39+
import org.springframework.data.mapping.ShadowedPropertyTypeWithCtor;
40+
import org.springframework.data.mapping.ShadowingPropertyType;
41+
import org.springframework.data.mapping.ShadowingPropertyTypeWithCtor;
3742
import org.springframework.data.mapping.model.BasicPersistentEntity;
3843
import org.springframework.data.mapping.model.SimpleTypeHolder;
3944
import org.springframework.data.util.ClassTypeInformation;
45+
import org.springframework.data.util.StreamUtils;
4046
import org.springframework.data.util.TypeInformation;
4147

4248
/**
@@ -45,6 +51,7 @@
4551
* @author Oliver Gierke
4652
* @author Thomas Darimont
4753
* @author Mark Paluch
54+
* @author Christoph Stobl
4855
*/
4956
public class AbstractMappingContextUnitTests {
5057

@@ -213,6 +220,37 @@ public void doesNotReturnPersistentEntityForCustomSimpleTypeProperty() {
213220
assertThat(context.getPersistentEntity(property)).isNull();
214221
}
215222

223+
@Test // DATACMNS-1509
224+
public void shouldIgnoreKotlinOverrideCtorPropertyInSuperClass() {
225+
226+
BasicPersistentEntity<Object, SamplePersistentProperty> entity = context
227+
.getPersistentEntity(ClassTypeInformation.from(ShadowingPropertyTypeWithCtor.class));
228+
entity.doWithProperties((PropertyHandler<SamplePersistentProperty>) property -> {
229+
assertThat(property.getField().getDeclaringClass()).isNotEqualTo(ShadowedPropertyTypeWithCtor.class);
230+
});
231+
}
232+
233+
@Test // DATACMNS-1509
234+
public void shouldIgnoreKotlinOverridePropertyInSuperClass() {
235+
236+
BasicPersistentEntity<Object, SamplePersistentProperty> entity = context
237+
.getPersistentEntity(ClassTypeInformation.from(ShadowingPropertyType.class));
238+
entity.doWithProperties((PropertyHandler<SamplePersistentProperty>) property -> {
239+
assertThat(property.getField().getDeclaringClass()).isNotEqualTo(ShadowedPropertyType.class);
240+
});
241+
}
242+
243+
@Test // DATACMNS-1509
244+
public void shouldStillIncludeNonKotlinShadowedPropertyInSuperClass() {
245+
246+
BasicPersistentEntity<Object, SamplePersistentProperty> entity = context
247+
.getPersistentEntity(ClassTypeInformation.from(ShadowingProperty.class));
248+
249+
assertThat(StreamUtils.createStreamFromIterator(entity.iterator())
250+
.filter(it -> it.getField().getDeclaringClass().equals(ShadowedProperty.class)).findFirst() //
251+
).isNotEmpty();
252+
}
253+
216254
private static void assertHasEntityFor(Class<?> type, SampleMappingContext context, boolean expected) {
217255

218256
boolean found = false;
@@ -252,4 +290,37 @@ static class Base {
252290
static class Extension extends Base {
253291
@Id String foo;
254292
}
293+
294+
static class ShadowedProperty {
295+
296+
private final String value;
297+
298+
ShadowedProperty(String value) {
299+
this.value = value;
300+
}
301+
302+
public String getValue() {
303+
return value;
304+
}
305+
}
306+
307+
static class ShadowingProperty extends ShadowedProperty {
308+
309+
private String value;
310+
311+
ShadowingProperty(String value) {
312+
super(value);
313+
this.value = value;
314+
}
315+
316+
public void setValue(String value) {
317+
this.value = value;
318+
}
319+
320+
@Override
321+
public String getValue() {
322+
return value;
323+
}
324+
}
325+
255326
}

0 commit comments

Comments
 (0)