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)
|
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]
|
[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`.
|
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]]
|
||||||
=== 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("class", null));
|
||||||
matches.add(new PropertyMatch("this\\$.*", null));
|
matches.add(new PropertyMatch("this\\$.*", null));
|
||||||
matches.add(new PropertyMatch("metaClass", "groovy.lang.MetaClass"));
|
matches.add(new PropertyMatch("metaClass", "groovy.lang.MetaClass"));
|
||||||
|
matches.add(new KotlinDataClassPropertyMatch(".*\\$delegate", null));
|
||||||
|
|
||||||
UNMAPPED_PROPERTIES = Streamable.of(matches);
|
UNMAPPED_PROPERTIES = Streamable.of(matches);
|
||||||
}
|
}
|
||||||
@@ -738,7 +739,7 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
|||||||
}
|
}
|
||||||
|
|
||||||
return UNMAPPED_PROPERTIES.stream()//
|
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()//
|
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}.
|
* 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 name must not be {@literal null}.
|
||||||
* @param type must not be {@literal null}.
|
* @param type must not be {@literal null}.
|
||||||
* @return
|
* @return
|
||||||
@@ -808,5 +829,40 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
|
|||||||
return true;
|
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.ShadowedPropertyTypeWithCtor;
|
||||||
import org.springframework.data.mapping.ShadowingPropertyType;
|
import org.springframework.data.mapping.ShadowingPropertyType;
|
||||||
import org.springframework.data.mapping.ShadowingPropertyTypeWithCtor;
|
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.BasicPersistentEntity;
|
||||||
|
import org.springframework.data.mapping.model.DataClassWithLazy;
|
||||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||||
import org.springframework.data.util.StreamUtils;
|
import org.springframework.data.util.StreamUtils;
|
||||||
import org.springframework.data.util.Streamable;
|
import org.springframework.data.util.Streamable;
|
||||||
@@ -179,6 +181,16 @@ class AbstractMappingContextUnitTests {
|
|||||||
assertThat(context.getPersistentEntity(SimpleDataClass.class)).isNotNull();
|
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
|
@Test // DATACMNS-1171
|
||||||
void shouldNotCreateEntityForSyntheticKotlinClass() {
|
void shouldNotCreateEntityForSyntheticKotlinClass() {
|
||||||
assertThat(context.getPersistentEntity(TypeCreatingSyntheticClass.class)).isNotNull();
|
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()) {
|
data class SingleSettableProperty constructor(val id: Double = Math.random()) {
|
||||||
val version: Int? = null
|
val version: Int? = null
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user