/*
 * Copyright 2011-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.mapping;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;

import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

Author:Graeme Rocher, Jon Brisbin, Oliver Gierke, Mark Paluch, Jens Schauder
/** * @author Graeme Rocher * @author Jon Brisbin * @author Oliver Gierke * @author Mark Paluch * @author Jens Schauder */
public interface PersistentProperty<P extends PersistentProperty<P>> {
Returns the PersistentEntity owning the current PersistentProperty.
Returns:never null.
/** * Returns the {@link PersistentEntity} owning the current {@link PersistentProperty}. * * @return never {@literal null}. */
PersistentEntity<?, P> getOwner();
The name of the property
Returns:The property name
/** * The name of the property * * @return The property name */
String getName();
The type of the property
Returns:The property type
/** * The type of the property * * @return The property type */
Class<?> getType();
Returns the TypeInformation of the property.
Returns:
/** * Returns the {@link TypeInformation} of the property. * * @return */
TypeInformation<?> getTypeInformation();
Returns the TypeInformation if the property references a PersistentEntity. Will return null in case it refers to a simple type. Will return Collection's component type or the Map's value type transparently.
Returns:
/** * Returns the {@link TypeInformation} if the property references a {@link PersistentEntity}. Will return * {@literal null} in case it refers to a simple type. Will return {@link Collection}'s component type or the * {@link Map}'s value type transparently. * * @return */
Iterable<? extends TypeInformation<?>> getPersistentEntityTypes();
Returns the getter method to access the property value if available. Might return null in case there is no getter method with a return type assignable to the actual property's type.
Returns:the getter method to access the property value if available, otherwise null.
/** * Returns the getter method to access the property value if available. Might return {@literal null} in case there is * no getter method with a return type assignable to the actual property's type. * * @return the getter method to access the property value if available, otherwise {@literal null}. */
@Nullable Method getGetter(); default Method getRequiredGetter() { Method getter = getGetter(); if (getter == null) { throw new IllegalArgumentException(String.format("No getter available for persistent property %s!", this)); } return getter; }
Returns the setter method to set a property value. Might return null in case there is no setter available.
Returns:the setter method to set a property value if available, otherwise null.
/** * Returns the setter method to set a property value. Might return {@literal null} in case there is no setter * available. * * @return the setter method to set a property value if available, otherwise {@literal null}. */
@Nullable Method getSetter(); default Method getRequiredSetter() { Method setter = getSetter(); if (setter == null) { throw new IllegalArgumentException(String.format("No setter available for persistent property %s!", this)); } return setter; }
Returns the wither Method to set a property value on a new object instance. Might return null in case there is no wither available.

Wither methods are property-bound instance methods that accept a single argument of the property type creating a new object instance.
class Person {
	final String id;
	final String name;
	// …
	Person withName(String name) {
		return new Person(this.id, name);
	}
}
Returns:the wither Method to set a property value on a new object instance if available, otherwise null.
Since:2.1
/** * Returns the wither {@link Method} to set a property value on a new object instance. Might return {@literal null} in * case there is no wither available. * <p/> * Wither {@link Method methods} are property-bound instance {@link Method methods} that accept a single argument of * the property type creating a new object instance. * * <pre class="code"> * class Person { * final String id; * final String name; * * // … * * Person withName(String name) { * return new Person(this.id, name); * } * } * </pre> * * @return the wither {@link Method} to set a property value on a new object instance if available, otherwise * {@literal null}. * @since 2.1 */
@Nullable Method getWither(); default Method getRequiredWither() { Method wither = getWither(); if (wither == null) { throw new IllegalArgumentException(String.format("No wither available for persistent property %s!", this)); } return wither; } @Nullable Field getField(); default Field getRequiredField() { Field field = getField(); if (field == null) { throw new IllegalArgumentException(String.format("No field backing persistent property %s!", this)); } return field; }
Returns:null if no expression defined.
/** * @return {@literal null} if no expression defined. */
@Nullable String getSpelExpression();
Returns:null if property is not part of an Association.
/** * @return {@literal null} if property is not part of an {@link Association}. */
@Nullable Association<P> getAssociation();
Get the Association of this property.
Throws:
Returns:never null.
/** * Get the {@link Association} of this property. * * @return never {@literal null}. * @throws IllegalStateException if not involved in an {@link Association}. */
default Association<P> getRequiredAssociation() { Association<P> association = getAssociation(); if (association != null) { return association; } throw new IllegalStateException("No association found!"); }
Returns whether the type of the PersistentProperty is actually to be regarded as PersistentEntity in turn.
Returns:true a PersistentEntity.
/** * Returns whether the type of the {@link PersistentProperty} is actually to be regarded as {@link PersistentEntity} * in turn. * * @return {@literal true} a {@link PersistentEntity}. */
boolean isEntity();
Returns whether the property is a potential identifier property of the owning PersistentEntity. This method is mainly used by PersistentEntity implementation to discover id property candidates on PersistentEntity creation you should rather call PersistentEntity.isIdProperty(PersistentProperty) to determine whether the current property is the id property of that PersistentEntity under consideration.
Returns:true if the id property.
/** * Returns whether the property is a <em>potential</em> identifier property of the owning {@link PersistentEntity}. * This method is mainly used by {@link PersistentEntity} implementation to discover id property candidates on * {@link PersistentEntity} creation you should rather call {@link PersistentEntity#isIdProperty(PersistentProperty)} * to determine whether the current property is the id property of that {@link PersistentEntity} under consideration. * * @return {@literal true} if the {@literal id} property. */
boolean isIdProperty();
Returns whether the current property is a potential version property of the owning PersistentEntity. This method is mainly used by PersistentEntity implementation to discover version property candidates on PersistentEntity creation you should rather call PersistentEntity.isVersionProperty(PersistentProperty) to determine whether the current property is the version property of that PersistentEntity under consideration.
Returns:
/** * Returns whether the current property is a <em>potential</em> version property of the owning * {@link PersistentEntity}. This method is mainly used by {@link PersistentEntity} implementation to discover version * property candidates on {@link PersistentEntity} creation you should rather call * {@link PersistentEntity#isVersionProperty(PersistentProperty)} to determine whether the current property is the * version property of that {@link PersistentEntity} under consideration. * * @return */
boolean isVersionProperty();
Returns whether the property is a Collection, Iterable or an array.
Returns:
/** * Returns whether the property is a {@link Collection}, {@link Iterable} or an array. * * @return */
boolean isCollectionLike();
Returns whether the property is a Map.
Returns:
/** * Returns whether the property is a {@link Map}. * * @return */
boolean isMap();
Returns whether the property is an array.
Returns:
/** * Returns whether the property is an array. * * @return */
boolean isArray();
Returns whether the property is transient.
Returns:
/** * Returns whether the property is transient. * * @return */
boolean isTransient();
Returns whether the current property is writable, i.e. if the value held for it shall be written to the data store.
Returns:
Since:1.9
/** * Returns whether the current property is writable, i.e. if the value held for it shall be written to the data store. * * @return * @since 1.9 */
boolean isWritable();
Returns whether the current property is immutable, i.e. if there is no setter or the backing Field is final.
See Also:
Returns:
Since:2.1
/** * Returns whether the current property is immutable, i.e. if there is no setter or the backing {@link Field} is * {@code final}. * * @return * @see java.lang.reflect.Modifier#isFinal(int) * @since 2.1 */
boolean isImmutable();
Returns whether the property is an Association.
Returns:
/** * Returns whether the property is an {@link Association}. * * @return */
boolean isAssociation();
Returns the component type of the type if it is a Collection. Will return the type of the key if the property is a Map.
Returns:the component type, the map's key type or null if neither Collection nor Map.
/** * Returns the component type of the type if it is a {@link java.util.Collection}. Will return the type of the key if * the property is a {@link java.util.Map}. * * @return the component type, the map's key type or {@literal null} if neither {@link java.util.Collection} nor * {@link java.util.Map}. */
@Nullable Class<?> getComponentType();
Returns the raw type as it's pulled from from the reflected property.
Returns:the raw type of the property.
/** * Returns the raw type as it's pulled from from the reflected property. * * @return the raw type of the property. */
Class<?> getRawType();
Returns the type of the values if the property is a Map.
Returns:the map's value type or null if no Map
/** * Returns the type of the values if the property is a {@link java.util.Map}. * * @return the map's value type or {@literal null} if no {@link java.util.Map} */
@Nullable Class<?> getMapValueType();
Returns the actual type of the property. This will be the original property type if no generics were used, the component type for collection-like types and arrays as well as the value type for map properties.
Returns:
/** * Returns the actual type of the property. This will be the original property type if no generics were used, the * component type for collection-like types and arrays as well as the value type for map properties. * * @return */
Class<?> getActualType();
Looks up the annotation of the given type on the PersistentProperty. Will inspect accessors and the potentially backing field and traverse accessor methods to potentially available super types.
Params:
  • annotationType – the annotation to look up, must not be null.
See Also:
Returns:the annotation of the given type. Can be null.
/** * Looks up the annotation of the given type on the {@link PersistentProperty}. Will inspect accessors and the * potentially backing field and traverse accessor methods to potentially available super types. * * @param annotationType the annotation to look up, must not be {@literal null}. * @return the annotation of the given type. Can be {@literal null}. * @see AnnotationUtils#findAnnotation(Method, Class) */
@Nullable <A extends Annotation> A findAnnotation(Class<A> annotationType);
Looks up the annotation of the given type on the PersistentProperty. Will inspect accessors and the potentially backing field and traverse accessor methods to potentially available super types.
Params:
  • annotationType – the annotation to look up, must not be null.
Throws:
Returns:the annotation of the given type.
Since:2.0
/** * Looks up the annotation of the given type on the {@link PersistentProperty}. Will inspect accessors and the * potentially backing field and traverse accessor methods to potentially available super types. * * @param annotationType the annotation to look up, must not be {@literal null}. * @return the annotation of the given type. * @throws IllegalStateException if the required {@code annotationType} is not found. * @since 2.0 */
default <A extends Annotation> A getRequiredAnnotation(Class<A> annotationType) throws IllegalStateException { A annotation = findAnnotation(annotationType); if (annotation != null) { return annotation; } throw new IllegalStateException( String.format("Required annotation %s not found for %s!", annotationType, getName())); }
Looks up the annotation of the given type on the property and the owning type if no annotation can be found on it. Useful to lookup annotations that can be configured on the type but overridden on an individual property.
Params:
  • annotationType – must not be null.
Returns:the annotation of the given type. Can be null.
/** * Looks up the annotation of the given type on the property and the owning type if no annotation can be found on it. * Useful to lookup annotations that can be configured on the type but overridden on an individual property. * * @param annotationType must not be {@literal null}. * @return the annotation of the given type. Can be {@literal null}. */
@Nullable <A extends Annotation> A findPropertyOrOwnerAnnotation(Class<A> annotationType);
Returns whether the PersistentProperty has an annotation of the given type.
Params:
  • annotationType – the annotation to lookup, must not be null.
Returns:whether the PersistentProperty has an annotation of the given type.
/** * Returns whether the {@link PersistentProperty} has an annotation of the given type. * * @param annotationType the annotation to lookup, must not be {@literal null}. * @return whether the {@link PersistentProperty} has an annotation of the given type. */
boolean isAnnotationPresent(Class<? extends Annotation> annotationType);
Returns whether property access shall be used for reading the property value. This means it will use the getter instead of field access.
Returns:
/** * Returns whether property access shall be used for reading the property value. This means it will use the getter * instead of field access. * * @return */
boolean usePropertyAccess();
Returns whether the actual type of the property carries the given annotation.
Params:
  • annotationType – must not be null.
See Also:
Returns:
Since:2.1
/** * Returns whether the actual type of the property carries the given annotation. * * @param annotationType must not be {@literal null}. * @return * @since 2.1 * @see #getActualType() */
default boolean hasActualTypeAnnotation(Class<? extends Annotation> annotationType) { Assert.notNull(annotationType, "Annotation type must not be null!"); return AnnotatedElementUtils.hasAnnotation(getActualType(), annotationType); }
Return the type the property refers to in case it's an association.
Returns:the type the property refers to in case it's an association or null in case it's not an association, the target entity type is not explicitly defined (either explicitly or through the property type itself).
Since:2.1
/** * Return the type the property refers to in case it's an association. * * @return the type the property refers to in case it's an association or {@literal null} in case it's not an * association, the target entity type is not explicitly defined (either explicitly or through the property * type itself). * @since 2.1 */
@Nullable Class<?> getAssociationTargetType(); }