Filter delegated properties for Kotlin data classes.
We now filter delegated properties (such as lazy) from being managed as persistent properties. Closes #3112
This commit is contained in:
@@ -314,7 +314,7 @@ Consider the following `data` class `Person`:
|
||||
data class Person(val id: String, val name: String)
|
||||
----
|
||||
|
||||
The class above compiles to a typical class with an explicit constructor.We can customize this class by adding another constructor and annotate it with `@PersistenceCreator` to indicate a constructor preference:
|
||||
The class above compiles to a typical class with an explicit constructor. We can customize this class by adding another constructor and annotate it with `@PersistenceCreator` to indicate a constructor preference:
|
||||
|
||||
[source,kotlin]
|
||||
----
|
||||
@@ -335,6 +335,9 @@ data class Person(var id: String, val name: String = "unknown")
|
||||
|
||||
Every time the `name` parameter is either not part of the result or its value is `null`, then the `name` defaults to `unknown`.
|
||||
|
||||
NOTE: Delegated properties are not supported with Spring Data. The mapping metadata filters delegated properties for Kotlin Data classes.
|
||||
In all other cases you can exclude synthetic fields for delegated properties by annotating the property with `@delegate:org.springframework.data.annotation.Transient`.
|
||||
|
||||
[[property-population-of-kotlin-data-classes]]
|
||||
=== Property population of Kotlin data classes
|
||||
|
||||
|
||||
@@ -726,6 +726,7 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
matches.add(new PropertyMatch("class", null));
|
||||
matches.add(new PropertyMatch("this\\$.*", null));
|
||||
matches.add(new PropertyMatch("metaClass", "groovy.lang.MetaClass"));
|
||||
matches.add(new KotlinDataClassPropertyMatch(".*\\$delegate", null));
|
||||
|
||||
UNMAPPED_PROPERTIES = Streamable.of(matches);
|
||||
}
|
||||
@@ -738,7 +739,7 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
}
|
||||
|
||||
return UNMAPPED_PROPERTIES.stream()//
|
||||
.noneMatch(it -> it.matches(field.getName(), field.getType()));
|
||||
.noneMatch(it -> it.matches(field));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -756,7 +757,7 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
}
|
||||
|
||||
return UNMAPPED_PROPERTIES.stream()//
|
||||
.noneMatch(it -> it.matches(property.getName(), property.getType()));
|
||||
.noneMatch(it -> it.matches(property));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -788,6 +789,26 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
/**
|
||||
* Returns whether the given {@link Field} matches the defined {@link PropertyMatch}.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public boolean matches(Field field) {
|
||||
return matches(field.getName(), field.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link Property} matches the defined {@link PropertyMatch}.
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public boolean matches(Property property) {
|
||||
return matches(property.getName(), property.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given field name and type matches the defined {@link PropertyMatch}.
|
||||
*
|
||||
* @param name must not be {@literal null}.
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
@@ -808,5 +829,40 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Value object extension to {@link PropertyMatch} that matches for fields only for Kotlin data classes.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.2.8
|
||||
*/
|
||||
static class KotlinDataClassPropertyMatch extends PropertyMatch {
|
||||
|
||||
public KotlinDataClassPropertyMatch(@Nullable String namePattern, @Nullable String typeName) {
|
||||
super(namePattern, typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Field field) {
|
||||
|
||||
if (!KotlinReflectionUtils.isDataClass(field.getDeclaringClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.matches(field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Property property) {
|
||||
|
||||
Field field = property.getField().orElse(null);
|
||||
|
||||
if (field == null || !KotlinReflectionUtils.isDataClass(field.getDeclaringClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.matches(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,9 @@ 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.AbstractPersistentProperty;
|
||||
import org.springframework.data.mapping.model.BasicPersistentEntity;
|
||||
import org.springframework.data.mapping.model.DataClassWithLazy;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.util.StreamUtils;
|
||||
import org.springframework.data.util.Streamable;
|
||||
@@ -179,6 +181,16 @@ class AbstractMappingContextUnitTests {
|
||||
assertThat(context.getPersistentEntity(SimpleDataClass.class)).isNotNull();
|
||||
}
|
||||
|
||||
@Test // GH-3112
|
||||
void shouldIgnoreLazyFieldsForDataClasses() {
|
||||
|
||||
BasicPersistentEntity<Object, SamplePersistentProperty> entity = context
|
||||
.getRequiredPersistentEntity(DataClassWithLazy.class);
|
||||
|
||||
List<String> propertyNames = Streamable.of(entity).map(AbstractPersistentProperty::getName).toList();
|
||||
assertThat(propertyNames).containsOnly("amount", "currency");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1171
|
||||
void shouldNotCreateEntityForSyntheticKotlinClass() {
|
||||
assertThat(context.getPersistentEntity(TypeCreatingSyntheticClass.class)).isNotNull();
|
||||
|
||||
@@ -29,6 +29,13 @@ data class ExtendedDataClassKt(val id: Long, val name: String) {
|
||||
}
|
||||
}
|
||||
|
||||
data class DataClassWithLazy(
|
||||
val amount: Int,
|
||||
val currency: String,
|
||||
) {
|
||||
val foo by lazy { 123 }
|
||||
}
|
||||
|
||||
data class SingleSettableProperty constructor(val id: Double = Math.random()) {
|
||||
val version: Int? = null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user