Browse Source

Include transient properties in persistent entity metamodel.

pull/2985/head
Mark Paluch 2 years ago committed by Christoph Strobl
parent
commit
34f94dccd6
No known key found for this signature in database
GPG Key ID: E6054036D0C37A4B
  1. 54
      src/main/java/org/springframework/data/mapping/PersistentEntity.java
  2. 4
      src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java
  3. 30
      src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java
  4. 8
      src/main/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProvider.java
  5. 33
      src/test/java/org/springframework/data/mapping/model/BasicPersistentEntityUnitTests.java
  6. 5
      src/test/kotlin/org/springframework/data/mapping/model/DataClasses.kt

54
src/main/java/org/springframework/data/mapping/PersistentEntity.java

@ -49,8 +49,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It @@ -49,8 +49,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
*
* @return {@literal null} in case no suitable creation mechanism for automatic construction can be found. This
* usually indicates that the instantiation of the object of that persistent entity is done through either a
* customer {@link org.springframework.data.mapping.model.EntityInstantiator} or handled by custom
* conversion mechanisms entirely.
* customer {@link org.springframework.data.mapping.model.EntityInstantiator} or handled by custom conversion
* mechanisms entirely.
* @since 3.0
*/
@Nullable
@ -110,8 +110,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It @@ -110,8 +110,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
}
/**
* Returns the version property of the {@link PersistentEntity}. Can be {@literal null} in case no version property
* is available on the entity.
* Returns the version property of the {@link PersistentEntity}. Can be {@literal null} in case no version property is
* available on the entity.
*
* @return the version property of the {@link PersistentEntity}.
*/
@ -119,8 +119,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It @@ -119,8 +119,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
P getVersionProperty();
/**
* Returns the version property of the {@link PersistentEntity}. Can be {@literal null} in case no version property
* is available on the entity.
* Returns the version property of the {@link PersistentEntity}. Can be {@literal null} in case no version property is
* available on the entity.
*
* @return the version property of the {@link PersistentEntity}.
* @throws IllegalStateException if {@link PersistentEntity} does not define a {@literal version} property.
@ -140,7 +140,7 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It @@ -140,7 +140,7 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
/**
* Obtains a {@link PersistentProperty} instance by name.
*
* @param name The name of the property. Can be {@literal null}.
* @param name the name of the property. Can be {@literal null}.
* @return the {@link PersistentProperty} or {@literal null} if it doesn't exist.
*/
@Nullable
@ -186,6 +186,28 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It @@ -186,6 +186,28 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
*/
Iterable<P> getPersistentProperties(Class<? extends Annotation> annotationType);
/**
* Obtains a transient {@link PersistentProperty} instance by name. You can check with {@link #isTransient(String)}
* whether there is a transient property before calling this method.
*
* @param name the name of the property. Can be {@literal null}.
* @return the {@link PersistentProperty} or {@literal null} if it doesn't exist.
* @since 3.3
* @see #isTransient(String)
*/
@Nullable
P getTransientProperty(String name);
/**
* Returns whether the property is transient.
*
* @param property name of the property.
* @return {@code true} if the property is transient. Applies only for existing properties. {@code false} if the
* property does not exist or is not transient.
* @since 3.3
*/
boolean isTransient(String property);
/**
* Returns whether the {@link PersistentEntity} has an id property. If this call returns {@literal true},
* {@link #getIdProperty()} will return a non-{@literal null} value.
@ -210,8 +232,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It @@ -210,8 +232,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
Class<T> getType();
/**
* Returns the alias to be used when storing type information. Might be {@literal null} to indicate that there was
* no alias defined through the mapping metadata.
* Returns the alias to be used when storing type information. Might be {@literal null} to indicate that there was no
* alias defined through the mapping metadata.
*
* @return
*/
@ -241,8 +263,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It @@ -241,8 +263,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
void doWithProperties(SimplePropertyHandler handler);
/**
* Applies the given {@link AssociationHandler} to all {@link Association} contained in this
* {@link PersistentEntity}. The iteration order is undefined.
* Applies the given {@link AssociationHandler} to all {@link Association} contained in this {@link PersistentEntity}.
* The iteration order is undefined.
*
* @param handler must not be {@literal null}.
*/
@ -257,8 +279,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It @@ -257,8 +279,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
void doWithAssociations(SimpleAssociationHandler handler);
/**
* Applies the given {@link PropertyHandler} to both all {@link PersistentProperty}s as well as all inverse
* properties of all {@link Association}s. The iteration order is undefined.
* Applies the given {@link PropertyHandler} to both all {@link PersistentProperty}s as well as all inverse properties
* of all {@link Association}s. The iteration order is undefined.
*
* @param handler must not be {@literal null}.
* @since 2.5
@ -342,7 +364,7 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It @@ -342,7 +364,7 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
*
* @param bean must not be {@literal null}.
* @throws IllegalArgumentException in case the given bean is not an instance of the typ represented by the
* {@link PersistentEntity}.
* {@link PersistentEntity}.
* @return whether the given bean is considered a new instance.
*/
boolean isNew(Object bean);
@ -358,8 +380,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It @@ -358,8 +380,8 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
boolean isImmutable();
/**
* Returns whether the entity needs properties to be populated, i.e. if any property exists that's not initialized
* by the constructor.
* Returns whether the entity needs properties to be populated, i.e. if any property exists that's not initialized by
* the constructor.
*
* @return
* @since 2.1

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

@ -646,10 +646,6 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<? @@ -646,10 +646,6 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
P property = createPersistentProperty(input, entity, simpleTypeHolder);
if (property.isTransient()) {
return;
}
if (!input.isFieldBacked() && !property.usePropertyAccess()) {
return;
}

30
src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java

@ -72,11 +72,14 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> @@ -72,11 +72,14 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>>
private final @Nullable InstanceCreatorMetadata<P> creator;
private final TypeInformation<T> information;
private final List<P> properties;
private final List<P> transientProperties;
private final List<P> persistentPropertiesCache;
private final @Nullable Comparator<P> comparator;
private final Set<Association<P>> associations;
private final Map<String, P> propertyCache;
private final Map<String, P> transientPropertyCache;
private final Map<Class<? extends Annotation>, Optional<Annotation>> annotationCache;
private final MultiValueMap<Class<? extends Annotation>, P> propertyAnnotationCache;
@ -114,12 +117,14 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> @@ -114,12 +117,14 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>>
this.information = information;
this.properties = new ArrayList<>();
this.transientProperties = new ArrayList<>(0);
this.persistentPropertiesCache = new ArrayList<>();
this.comparator = comparator;
this.creator = InstanceCreatorMetadataDiscoverer.discover(this);
this.associations = comparator == null ? new HashSet<>() : new TreeSet<>(new AssociationComparator<>(comparator));
this.propertyCache = new HashMap<>(16, 1.0f);
this.transientPropertyCache = new HashMap<>(0, 1f);
this.annotationCache = new ConcurrentHashMap<>(16);
this.propertyAnnotationCache = CollectionUtils.toMultiValueMap(new ConcurrentHashMap<>(16));
this.propertyAccessorFactory = BeanWrapperPropertyAccessorFactory.INSTANCE;
@ -186,6 +191,18 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> @@ -186,6 +191,18 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>>
Assert.notNull(property, "Property must not be null");
if (property.isTransient()) {
if (transientProperties.contains(property)) {
return;
}
transientProperties.add(property);
transientPropertyCache.put(property.getName(), property);
return;
}
if (properties.contains(property)) {
return;
}
@ -279,6 +296,19 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> @@ -279,6 +296,19 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>>
return propertyCache.get(name);
}
@Override
public P getTransientProperty(String name) {
return transientPropertyCache.get(name);
}
@Override
public boolean isTransient(String property) {
P transientProperty = getTransientProperty(property);
return transientProperty != null && transientProperty.isTransient();
}
@Override
public Iterable<P> getPersistentProperties(Class<? extends Annotation> annotationType) {

8
src/main/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProvider.java

@ -17,6 +17,7 @@ package org.springframework.data.mapping.model; @@ -17,6 +17,7 @@ package org.springframework.data.mapping.model;
import org.jspecify.annotations.Nullable;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mapping.InstanceCreatorMetadata;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.Parameter;
@ -55,6 +56,13 @@ public class PersistentEntityParameterValueProvider<P extends PersistentProperty @@ -55,6 +56,13 @@ public class PersistentEntityParameterValueProvider<P extends PersistentProperty
return (T) parent;
}
if (parameter.getAnnotations().isPresent(Transient.class)) {
// parameter.getRawType().isPrimitive()
return null;
}
String name = parameter.getName();
if (name == null) {

33
src/test/java/org/springframework/data/mapping/model/BasicPersistentEntityUnitTests.java

@ -32,6 +32,8 @@ import java.util.stream.Stream; @@ -32,6 +32,8 @@ import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ -106,8 +108,7 @@ class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> { @@ -106,8 +108,7 @@ class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> {
@SuppressWarnings("unchecked")
void considersComparatorForPropertyOrder() {
var entity = createEntity(Person.class,
Comparator.comparing(PersistentProperty::getName));
var entity = createEntity(Person.class, Comparator.comparing(PersistentProperty::getName));
var lastName = (T) Mockito.mock(PersistentProperty.class);
when(lastName.getName()).thenReturn("lastName");
@ -199,8 +200,8 @@ class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> { @@ -199,8 +200,8 @@ class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> {
assertThat(accessor).isNotInstanceOf(BeanWrapper.class);
assertThat(accessor).isInstanceOfSatisfying(InstantiationAwarePropertyAccessor.class, it -> {
var delegateFunction = (Function<Object, PersistentPropertyAccessor<Object>>) ReflectionTestUtils
.getField(it, "delegateFunction");
var delegateFunction = (Function<Object, PersistentPropertyAccessor<Object>>) ReflectionTestUtils.getField(it,
"delegateFunction");
var delegate = delegateFunction.apply(value);
assertThat(delegate.getClass().getName()).contains("_Accessor_");
@ -360,6 +361,18 @@ class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> { @@ -360,6 +361,18 @@ class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> {
.forEach(it -> assertThat(createPopulatedPersistentEntity(it).requiresPropertyPopulation()).isFalse());
}
@ParameterizedTest // GH-1432
@ValueSource(classes = { WithTransient.class, RecordWithTransient.class, DataClassWithTransientProperty.class })
void includesTransientProperty(Class<?> classUnderTest) {
PersistentEntity<Object, ?> entity = createPopulatedPersistentEntity(classUnderTest);
assertThat(entity).extracting(PersistentProperty::getName).hasSize(1).containsOnly("firstname");
assertThat(entity.isTransient("firstname")).isFalse();
assertThat(entity.isTransient("lastname")).isTrue();
assertThat(entity.getTransientProperty("lastname").getName()).isEqualTo("lastname");
}
@Test // #2325
void doWithAllInvokesPropertyHandlerForBothAPropertiesAndAssociations() {
@ -476,6 +489,17 @@ class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> { @@ -476,6 +489,17 @@ class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> {
}
}
private static class WithTransient {
String firstname;
@Transient String lastname;
}
record RecordWithTransient(String firstname, @Transient String lastname) {
}
// #2325
static class WithAssociation {
@ -483,4 +507,5 @@ class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> { @@ -483,4 +507,5 @@ class BasicPersistentEntityUnitTests<T extends PersistentProperty<T>> {
String property;
@Reference WithAssociation association;
}
}

5
src/test/kotlin/org/springframework/data/mapping/model/DataClasses.kt

@ -18,6 +18,7 @@ package org.springframework.data.mapping.model @@ -18,6 +18,7 @@ package org.springframework.data.mapping.model
import org.jmolecules.ddd.types.AggregateRoot
import org.jmolecules.ddd.types.Identifier
import org.springframework.data.annotation.Id
import org.springframework.data.annotation.Transient
import java.time.LocalDateTime
/**
@ -55,6 +56,10 @@ data class SingleSettableProperty constructor(val id: Double = Math.random()) { @@ -55,6 +56,10 @@ data class SingleSettableProperty constructor(val id: Double = Math.random()) {
val version: Int? = null
}
// note: Kotlin ships also a @Transient annotation to indicate JVM's transient keyword.
data class DataClassWithTransientProperty(val firstname: String, @Transient val lastname: String)
data class DataClassWithTransientProperties(@Transient val foo: String = "foo", @Transient val bar: Int)
data class WithCustomCopyMethod(
val id: String?,
val userId: String,

Loading…
Cancel
Save