diff --git a/src/main/antora/modules/ROOT/pages/object-mapping.adoc b/src/main/antora/modules/ROOT/pages/object-mapping.adoc index fad8b4cca..380966c53 100644 --- a/src/main/antora/modules/ROOT/pages/object-mapping.adoc +++ b/src/main/antora/modules/ROOT/pages/object-mapping.adoc @@ -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 diff --git a/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java b/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java index ba8167c44..3c67ec7e9 100644 --- a/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java +++ b/src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java @@ -726,6 +726,7 @@ public abstract class AbstractMappingContext it.matches(field.getName(), field.getType())); + .noneMatch(it -> it.matches(field)); } /** @@ -756,7 +757,7 @@ public abstract class AbstractMappingContext it.matches(property.getName(), property.getType())); + .noneMatch(it -> it.matches(property)); } /** @@ -788,6 +789,26 @@ public abstract class AbstractMappingContext entity = context + .getRequiredPersistentEntity(DataClassWithLazy.class); + + List propertyNames = Streamable.of(entity).map(AbstractPersistentProperty::getName).toList(); + assertThat(propertyNames).containsOnly("amount", "currency"); + } + @Test // DATACMNS-1171 void shouldNotCreateEntityForSyntheticKotlinClass() { assertThat(context.getPersistentEntity(TypeCreatingSyntheticClass.class)).isNotNull(); diff --git a/src/test/kotlin/org/springframework/data/mapping/model/DataClasses.kt b/src/test/kotlin/org/springframework/data/mapping/model/DataClasses.kt index ac98c544c..d27659f6e 100644 --- a/src/test/kotlin/org/springframework/data/mapping/model/DataClasses.kt +++ b/src/test/kotlin/org/springframework/data/mapping/model/DataClasses.kt @@ -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 }