DATAJPA-658 - Polishing.

Moved identifier attribute detection into JpaMetamodel and refactored to Java 8 Stream based lookup. The identifier property calculation is now contained in a Lazy instance so that repeated calls reuse the same outcome.

Original pull request: #146.
This commit is contained in:
Oliver Gierke
2018-02-15 11:40:43 +01:00
parent dec978df09
commit c5dafc5720
2 changed files with 45 additions and 46 deletions

View File

@@ -35,9 +35,7 @@ import javax.persistence.OneToOne;
import javax.persistence.OrderColumn;
import javax.persistence.Transient;
import javax.persistence.Version;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.metamodel.SingularAttribute;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.annotation.AccessType.Type;
@@ -48,6 +46,7 @@ import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -97,7 +96,8 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
private final @Nullable TypeInformation<?> associationTargetType;
private final boolean updateable;
private final JpaMetamodel metamodel;
private final EntityType<?> entityType;
private final Lazy<Boolean> isIdProperty;
/**
* Creates a new {@link JpaPersistentPropertyImpl}
@@ -114,33 +114,13 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
Assert.notNull(metamodel, "Metamodel must not be null!");
this.entityType = tryResolveEntityType(metamodel, getOwner().getType());
this.usePropertyAccess = detectPropertyAccess();
this.associationTargetType = detectAssociationTargetType();
this.updateable = detectUpdatability();
this.metamodel = new JpaMetamodel(metamodel);
}
/**
* Return the {@link EntityType} for the given Entity {@link Class} or null if given {@code type} is not a mapped
* entity.
*
* @param metamodel
* @param type
* @return
*/
private EntityType<?> tryResolveEntityType(Metamodel metamodel, Class<?> type) {
EntityType<?> ownerEntityType = null;
for (EntityType<?> entityType : metamodel.getEntities()) {
if (entityType.getJavaType().equals(type)) {
ownerEntityType = entityType;
break;
}
}
return ownerEntityType;
this.isIdProperty = Lazy.of(() -> ID_ANNOTATIONS.stream().anyMatch(it -> isAnnotationPresent(it)) //
|| this.metamodel.isSingleIdAttribute(getOwner().getType(), getName(), getType()));
}
/*
@@ -168,27 +148,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
*/
@Override
public boolean isIdProperty() {
boolean isId ID_ANNOTATIONS.stream().anyMatch(it -> isAnnotationPresent(it));
if (isId) {
return true;
}
if (isIdPropertyCandidateAccordingToMetaModel()) {
SingularAttribute<?, ?> idAttribute = entityType.getId(getType());
if (idAttribute.getName().equals(getName())) {
return true;
}
}
return false;
}
private boolean isIdPropertyCandidateAccordingToMetaModel() {
return entityType != null && entityType.hasSingleIdAttribute()
&& entityType.getIdType().getJavaType().equals(getType());
return isIdProperty.get();
}
/*

View File

@@ -21,8 +21,10 @@ import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.metamodel.SingularAttribute;
import org.springframework.util.Assert;
@@ -63,6 +65,25 @@ public class JpaMetamodel {
return getManagedTypes().contains(type);
}
/**
* Returns whether the attribute of given name and type is the single identifier attribute of the given entity.
*
* @param entity must not be {@literal null}.
* @param name must not be {@literal null}.
* @param attributeType must not be {@literal null}.
* @return
*/
public boolean isSingleIdAttribute(Class<?> entity, String name, Class<?> attributeType) {
return metamodel.getEntities().stream() //
.filter(it -> it.getJavaType().equals(entity)) //
.findFirst() //
.flatMap(it -> getSingularIdAttribute(it)) //
.filter(it -> it.getJavaType().equals(attributeType)) //
.map(it -> it.getName().equals(name)) //
.orElse(false);
}
/**
* Returns all types managed by the backing {@link Metamodel}. Skips {@link ManagedType} instances that return
* {@literal null} for calls to {@link ManagedType#getJavaType()}.
@@ -91,4 +112,22 @@ public class JpaMetamodel {
return this.managedTypes.get();
}
/**
* Returns the {@link SingularAttribute} representing the identifier of the given {@link EntityType} if it contains a
* singular one.
*
* @param entityType must not be {@literal null}.
* @return
*/
private static Optional<? extends SingularAttribute<?, ?>> getSingularIdAttribute(EntityType<?> entityType) {
if (!entityType.hasSingleIdAttribute()) {
return Optional.empty();
}
return entityType.getSingularAttributes().stream() //
.filter(SingularAttribute::isId) //
.findFirst();
}
}