diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java index fbf9a0c16..167d141d0 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java @@ -16,11 +16,13 @@ package org.springframework.data.jpa.mapping; import java.util.Set; +import java.util.function.Predicate; import javax.persistence.metamodel.ManagedType; import javax.persistence.metamodel.Metamodel; import org.springframework.data.jpa.provider.PersistenceProvider; +import org.springframework.data.mapping.PersistentPropertyPaths; import org.springframework.data.mapping.context.AbstractMappingContext; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.Property; @@ -92,6 +94,18 @@ public class JpaMetamodelMappingContext return getMetamodelFor(type.getType()) != null; } + /** + * We customize the lookup of {@link PersistentPropertyPaths} by also traversing properties that are embeddables. + * + * @see org.springframework.data.mapping.context.AbstractMappingContext#findPersistentPropertyPaths(java.lang.Class, + * java.util.function.Predicate) + */ + @Override + public PersistentPropertyPaths findPersistentPropertyPaths(Class type, + Predicate predicate) { + return doFindPersistentPropertyPaths(type, predicate, it -> it.isEmbeddable()); + } + /** * Returns the {@link Metamodel} aware of the given type. * diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java index 712964364..ec6e4528e 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentProperty.java @@ -25,4 +25,11 @@ import org.springframework.data.mapping.PersistentProperty; */ public interface JpaPersistentProperty extends PersistentProperty { + /** + * Return whether the property is considered embeddable. + * + * @return + * @since 2.1 + */ + boolean isEmbeddable(); } diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index ac18f1437..521c8b1eb 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -75,7 +75,6 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty isIdProperty; + private final Lazy isAssociation; /** * Creates a new {@link JpaPersistentPropertyImpl} @@ -114,6 +114,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty ASSOCIATION_ANNOTATIONS.stream().anyMatch(this::isAnnotationPresent)); this.usePropertyAccess = detectPropertyAccess(); this.associationTargetType = detectAssociationTargetType(); this.updateable = detectUpdatability(); @@ -132,14 +133,16 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty> getPersistentEntityType() { - return associationTargetType != null ? Collections.singleton(associationTargetType) - : super.getPersistentEntityType(); + public Iterable> getPersistentEntityTypes() { + + return associationTargetType != null // + ? Collections.singleton(associationTargetType) // + : super.getPersistentEntityTypes(); } /* @@ -166,14 +169,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty annotationType : ASSOCIATION_ANNOTATIONS) { - if (findAnnotation(annotationType) != null) { - return true; - } - } - - return getType().isAnnotationPresent(Embeddable.class); + return isAssociation.get(); } /* @@ -221,6 +217,15 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty persistentEntity = context.getPersistentEntity(User.class); + JpaPersistentProperty property = persistentEntity.getPersistentProperty("address"); + + assertThat(property.isEmbeddable()).isTrue(); + } + + @Test // DATAJPA-1320 + public void traversesEmbeddablesButNoOtherMappingAnnotations() { + + PersistentPropertyPaths paths = // + context.findPersistentPropertyPaths(User.class, __ -> true); + + assertThat(paths.contains("address.city")).isTrue(); + + // Exists but is not selected + assertThat(context.getPersistentPropertyPath("colleagues.firstname", User.class)).isNotNull(); + assertThat(paths.contains("colleagues.firstname")).isFalse(); + } } diff --git a/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java b/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java index 04c829ff2..1ab542281 100644 --- a/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java @@ -82,13 +82,13 @@ public class JpaPersistentPropertyImplUnitTests { } @Test // DATAJPA-484 - public void considersEmbeddablePropertyAnAssociation() { - assertThat(entity.getRequiredPersistentProperty("embeddable").isAssociation(), is(true)); + public void doesNotConsiderAnEmbeddablePropertyAnAssociation() { + assertThat(entity.getRequiredPersistentProperty("embeddable").isAssociation(), is(false)); } @Test // DATAJPA-484 - public void considersEmbeddedPropertyAnAssociation() { - assertThat(entity.getRequiredPersistentProperty("embedded").isAssociation(), is(true)); + public void doesNotConsiderAnEmbeddedPropertyAnAssociation() { + assertThat(entity.getRequiredPersistentProperty("embedded").isAssociation(), is(false)); } @Test // DATAJPA-619 @@ -137,7 +137,7 @@ public class JpaPersistentPropertyImplUnitTests { assertThat(property.getType(), is(typeCompatibleWith(Api.class))); assertThat(property.getActualType(), is(typeCompatibleWith(Implementation.class))); - Iterable> entityType = property.getPersistentEntityType(); + Iterable> entityType = property.getPersistentEntityTypes(); assertThat(entityType.iterator().hasNext(), is(true)); assertThat(entityType.iterator().next(), is((TypeInformation) ClassTypeInformation.from(Implementation.class))); }