DATAJPA-1320 - Adapt to API changes in Spring Data Commons.

JpaPersistentProperty now exposes an ….isEmbeddable(), currently used in JpaMetamodelMappingContext implementation to make sure that embeddable types are traversed when it comes to PersistentPropertyPath detection. That in turn is now used for the detection of auditing metdata, so that auditing annotations can also be used on embeddables.

Lazified the decision whether a property is an association which prevents repeated annotation lookups.

Related tickets: DATACMNS-1275.
This commit is contained in:
Oliver Gierke
2018-04-09 11:43:07 +02:00
parent 53c6a577de
commit a0634ce94a
5 changed files with 70 additions and 20 deletions

View File

@@ -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 <T> PersistentPropertyPaths<T, JpaPersistentProperty> findPersistentPropertyPaths(Class<T> type,
Predicate<? super JpaPersistentProperty> predicate) {
return doFindPersistentPropertyPaths(type, predicate, it -> it.isEmbeddable());
}
/**
* Returns the {@link Metamodel} aware of the given type.
*

View File

@@ -25,4 +25,11 @@ import org.springframework.data.mapping.PersistentProperty;
*/
public interface JpaPersistentProperty extends PersistentProperty<JpaPersistentProperty> {
/**
* Return whether the property is considered embeddable.
*
* @return
* @since 2.1
*/
boolean isEmbeddable();
}

View File

@@ -75,7 +75,6 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
annotations.add(OneToOne.class);
annotations.add(ManyToMany.class);
annotations.add(ManyToOne.class);
annotations.add(Embedded.class);
ASSOCIATION_ANNOTATIONS = Collections.unmodifiableSet(annotations);
@@ -98,6 +97,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
private final JpaMetamodel metamodel;
private final Lazy<Boolean> isIdProperty;
private final Lazy<Boolean> isAssociation;
/**
* Creates a new {@link JpaPersistentPropertyImpl}
@@ -114,6 +114,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
Assert.notNull(metamodel, "Metamodel must not be null!");
this.isAssociation = Lazy.of(() -> ASSOCIATION_ANNOTATIONS.stream().anyMatch(this::isAnnotationPresent));
this.usePropertyAccess = detectPropertyAccess();
this.associationTargetType = detectAssociationTargetType();
this.updateable = detectUpdatability();
@@ -132,14 +133,16 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
return associationTargetType != null ? associationTargetType.getType() : super.getActualType();
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#getPersistentEntityType()
* @see org.springframework.data.mapping.PersistentProperty#getPersistentEntityTypes()
*/
@Override
public Iterable<? extends TypeInformation<?>> getPersistentEntityType() {
return associationTargetType != null ? Collections.singleton(associationTargetType)
: super.getPersistentEntityType();
public Iterable<? extends TypeInformation<?>> getPersistentEntityTypes() {
return associationTargetType != null //
? Collections.singleton(associationTargetType) //
: super.getPersistentEntityTypes();
}
/*
@@ -166,14 +169,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
*/
@Override
public boolean isAssociation() {
for (Class<? extends Annotation> 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<JpaPer
return updateable && super.isWritable();
}
/*
* (non-Javadoc)
* @see org.springframework.data.jpa.mapping.JpaPersistentProperty#isEmbeddable()
*/
@Override
public boolean isEmbeddable() {
return isAnnotationPresent(Embedded.class) || hasActualTypeAnnotation(Embeddable.class);
}
/**
* Looks up both Spring Data's and JPA's access type definition annotations on the property or type level to determine
* the access type to be used. Will consider property-level annotations over type-level ones, favoring the Spring Data

View File

@@ -15,8 +15,9 @@
*/
package org.springframework.data.jpa.mapping;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.junit.Assert.assertThat;
import java.util.Collections;
@@ -39,6 +40,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.jpa.repository.sample.CategoryRepository;
import org.springframework.data.jpa.repository.sample.ProductRepository;
import org.springframework.data.mapping.IdentifierAccessor;
import org.springframework.data.mapping.PersistentPropertyPaths;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
@@ -160,4 +162,26 @@ public class JpaMetamodelMappingContextIntegrationTests {
assertThat(entity.getIdProperty(), is(notNullValue()));
}
@Test // DATAJPA-1320
public void detectsEmbeddableProperty() {
JpaPersistentEntity<?> persistentEntity = context.getPersistentEntity(User.class);
JpaPersistentProperty property = persistentEntity.getPersistentProperty("address");
assertThat(property.isEmbeddable()).isTrue();
}
@Test // DATAJPA-1320
public void traversesEmbeddablesButNoOtherMappingAnnotations() {
PersistentPropertyPaths<User, JpaPersistentProperty> 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();
}
}

View File

@@ -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<? extends TypeInformation<?>> entityType = property.getPersistentEntityType();
Iterable<? extends TypeInformation<?>> entityType = property.getPersistentEntityTypes();
assertThat(entityType.iterator().hasNext(), is(true));
assertThat(entityType.iterator().next(), is((TypeInformation) ClassTypeInformation.from(Implementation.class)));
}