DATAJPA-1392 - Further improvements in Metamodel handling in MappingContext implementation.

We now cache the JpaMetamodel instances created for JPA Metamodels to avoid repeated iterations over all managed types. Abstracted type and JpaMetamodel lookups into a Metamodels value type. Lazified JpaPersistentPropertyImpl.isEntity(…) to avoid to keep a reference of the JpaMetamodel around.
This commit is contained in:
Oliver Gierke
2018-08-09 15:31:31 +02:00
parent de0a510c15
commit 3a03a66112
9 changed files with 87 additions and 54 deletions

View File

@@ -22,6 +22,7 @@ import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.jpa.util.JpaMetamodel;
import org.springframework.data.mapping.PersistentPropertyPaths;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.context.MappingContext;
@@ -42,7 +43,7 @@ import org.springframework.util.Assert;
public class JpaMetamodelMappingContext
extends AbstractMappingContext<JpaPersistentEntityImpl<?>, JpaPersistentProperty> {
private final Set<Metamodel> models;
private final Metamodels models;
private final PersistenceProvider persistenceProvider;
/**
@@ -55,7 +56,7 @@ public class JpaMetamodelMappingContext
Assert.notNull(models, "JPA metamodel must not be null!");
Assert.notEmpty(models, "At least one JPA metamodel must be present!");
this.models = models;
this.models = new Metamodels(models);
this.persistenceProvider = PersistenceProvider.fromMetamodel(models.iterator().next());
}
@@ -65,10 +66,7 @@ public class JpaMetamodelMappingContext
*/
@Override
protected <T> JpaPersistentEntityImpl<?> createPersistentEntity(TypeInformation<T> typeInformation) {
Metamodel metamodel = getMetamodelFor(typeInformation.getType());
return new JpaPersistentEntityImpl<T>(typeInformation, persistenceProvider, metamodel);
return new JpaPersistentEntityImpl<T>(typeInformation, persistenceProvider, models.getMetamodel(typeInformation));
}
/*
@@ -87,7 +85,7 @@ public class JpaMetamodelMappingContext
*/
@Override
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
return getMetamodelFor(type.getType()) != null;
return models.isMetamodelManagedType(type);
}
/**
@@ -103,32 +101,71 @@ public class JpaMetamodelMappingContext
}
/**
* Returns the {@link Metamodel} aware of the given type.
* A wrapper for a set of JPA {@link Metamodel} instances to simplify lookups of {@link JpaMetamodel} instances and
* managed type checks.
*
* @param type must not be {@literal null}.
* @return can be {@literal null}.
* @author Oliver Gierke
*/
@Nullable
private Metamodel getMetamodelFor(Class<?> type) {
private static class Metamodels {
for (Metamodel model : models) {
private final Set<Metamodel> metamodels;
try {
model.managedType(type);
return model;
} catch (IllegalArgumentException o_O) {
private Metamodels(Set<Metamodel> metamodels) {
this.metamodels = metamodels;
}
// Fall back to inspect *all* managed types manually as Metamodel.managedType(…) only
// returns for entities, embeddables and managed supperclasses.
/**
* Returns the {@link JpaMetamodel} for the given type.
*
* @param type must not be {@literal null}.
* @return
*/
@Nullable
public JpaMetamodel getMetamodel(TypeInformation<?> type) {
for (ManagedType<?> managedType : model.getManagedTypes()) {
if (type.equals(managedType.getJavaType())) {
return model;
Metamodel metamodel = getMetamodelFor(type.getType());
return metamodel == null ? null : JpaMetamodel.of(metamodel);
}
/**
* Retruns whether the given type is managed by one of the underlying {@link Metamodel} instances.
*
* @param type must not be {@literal null}.
* @return
*/
public boolean isMetamodelManagedType(TypeInformation<?> type) {
return getMetamodelFor(type.getType()) != null;
}
/**
* Returns the {@link Metamodel} aware of the given type.
*
* @param type must not be {@literal null}.
* @return can be {@literal null}.
*/
@Nullable
private Metamodel getMetamodelFor(Class<?> type) {
for (Metamodel model : metamodels) {
try {
model.managedType(type);
return model;
} catch (IllegalArgumentException o_O) {
// Fall back to inspect *all* managed types manually as Metamodel.managedType(…) only
// returns for entities, embeddables and managed supperclasses.
for (ManagedType<?> managedType : model.getManagedTypes()) {
if (type.equals(managedType.getJavaType())) {
return model;
}
}
}
}
}
return null;
return null;
}
}
}

View File

@@ -17,10 +17,9 @@ package org.springframework.data.jpa.mapping;
import java.util.Comparator;
import javax.persistence.metamodel.Metamodel;
import org.springframework.data.annotation.Version;
import org.springframework.data.jpa.provider.ProxyIdAccessor;
import org.springframework.data.jpa.util.JpaMetamodel;
import org.springframework.data.mapping.IdentifierAccessor;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.mapping.model.IdPropertyIdentifierAccessor;
@@ -45,15 +44,17 @@ class JpaPersistentEntityImpl<T> extends BasicPersistentEntity<T, JpaPersistentP
+ javax.persistence.Version.class.getName() + " to trigger optimistic locking correctly!";
private final ProxyIdAccessor proxyIdAccessor;
private final Metamodel metamodel;
private final JpaMetamodel metamodel;
/**
* Creates a new {@link JpaPersistentEntityImpl} using the given {@link TypeInformation} and {@link Comparator}.
*
* @param information must not be {@literal null}.
* @param proxyIdAccessor must not be {@literal null}.
* @param metamodel must not be {@literal null}.
*/
public JpaPersistentEntityImpl(TypeInformation<T> information, ProxyIdAccessor proxyIdAccessor, Metamodel metamodel) {
public JpaPersistentEntityImpl(TypeInformation<T> information, ProxyIdAccessor proxyIdAccessor,
JpaMetamodel metamodel) {
super(information, null);
@@ -96,7 +97,7 @@ class JpaPersistentEntityImpl<T> extends BasicPersistentEntity<T, JpaPersistentP
}
}
Metamodel getMetamodel() {
JpaMetamodel getMetamodel() {
return metamodel;
}

View File

@@ -21,20 +21,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderColumn;
import javax.persistence.Transient;
import javax.persistence.Version;
import javax.persistence.*;
import javax.persistence.metamodel.Metamodel;
import org.springframework.core.annotation.AnnotationUtils;
@@ -94,10 +81,10 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
private final @Nullable Boolean usePropertyAccess;
private final @Nullable TypeInformation<?> associationTargetType;
private final boolean updateable;
private final JpaMetamodel metamodel;
private final Lazy<Boolean> isIdProperty;
private final Lazy<Boolean> isAssociation;
private final Lazy<Boolean> isEntity;
/**
* Creates a new {@link JpaPersistentPropertyImpl}
@@ -107,7 +94,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
* @param owner must not be {@literal null}.
* @param simpleTypeHolder must not be {@literal null}.
*/
public JpaPersistentPropertyImpl(Metamodel metamodel, Property property,
public JpaPersistentPropertyImpl(JpaMetamodel metamodel, Property property,
PersistentEntity<?, JpaPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
super(property, owner, simpleTypeHolder);
@@ -118,10 +105,10 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
this.usePropertyAccess = detectPropertyAccess();
this.associationTargetType = detectAssociationTargetType();
this.updateable = detectUpdatability();
this.metamodel = new JpaMetamodel(metamodel);
this.isIdProperty = Lazy.of(() -> ID_ANNOTATIONS.stream().anyMatch(it -> isAnnotationPresent(it)) //
|| this.metamodel.isSingleIdAttribute(getOwner().getType(), getName(), getType()));
|| metamodel.isSingleIdAttribute(getOwner().getType(), getName(), getType()));
this.isEntity = Lazy.of(() -> metamodel.isJpaManaged(getActualType()));
}
/*
@@ -160,7 +147,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
*/
@Override
public boolean isEntity() {
return metamodel.isJpaManaged(getActualType());
return isEntity.get();
}
/*

View File

@@ -84,7 +84,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
this.method = method;
this.em = em;
this.metamodel = new JpaMetamodel(em.getMetamodel());
this.metamodel = JpaMetamodel.of(em.getMetamodel());
this.provider = PersistenceProvider.fromEntityManager(em);
}

View File

@@ -325,7 +325,7 @@ public class JpaMetamodelEntityInformation<T, ID> extends JpaEntityInformationSu
IdentifierDerivingDirectFieldAccessFallbackBeanWrapper(Class<?> type, Metamodel metamodel) {
super(type);
this.metamodel = metamodel;
this.jpaMetamodel = new JpaMetamodel(metamodel);
this.jpaMetamodel = JpaMetamodel.of(metamodel);
}
/**

View File

@@ -246,7 +246,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
Assert.notNull(em, "EntityManager must not be null!");
this.metamodel = new JpaMetamodel(em.getMetamodel());
this.metamodel = JpaMetamodel.of(em.getMetamodel());
}
/*

View File

@@ -16,6 +16,8 @@
package org.springframework.data.jpa.util;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.persistence.metamodel.EntityType;
@@ -35,6 +37,8 @@ import org.springframework.util.Assert;
*/
public class JpaMetamodel {
private static final Map<Metamodel, JpaMetamodel> CACHE = new HashMap<>(4);
private final Metamodel metamodel;
private Lazy<Collection<Class<?>>> managedTypes;
@@ -44,7 +48,7 @@ public class JpaMetamodel {
*
* @param metamodel must not be {@literal null}.
*/
public JpaMetamodel(Metamodel metamodel) {
private JpaMetamodel(Metamodel metamodel) {
Assert.notNull(metamodel, "Metamodel must not be null!");
@@ -55,6 +59,10 @@ public class JpaMetamodel {
.collect(StreamUtils.toUnmodifiableSet()));
}
public static JpaMetamodel of(Metamodel metamodel) {
return CACHE.computeIfAbsent(metamodel, JpaMetamodel::new);
}
/**
* Returns whether the given type is managed by the backing JPA {@link Metamodel}.
*

View File

@@ -49,7 +49,7 @@ import org.springframework.data.util.TypeInformation;
* @author Oliver Gierke
* @author Greg Turnquist
*/
@RunWith(MockitoJUnitRunner.class)
@RunWith(MockitoJUnitRunner.Silent.class)
public class JpaPersistentPropertyImplUnitTests {
@Mock Metamodel model;

View File

@@ -45,6 +45,6 @@ public class JpaMetamodelUnitTests {
doReturn(Collections.singleton(type)).when(metamodel).getEntities();
assertThat(new JpaMetamodel(metamodel).isSingleIdAttribute(Object.class, "id", Object.class)).isFalse();
assertThat(JpaMetamodel.of(metamodel).isSingleIdAttribute(Object.class, "id", Object.class)).isFalse();
}
}