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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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}.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user