From f2d27809b75fb47a555ae562894f789ef9fda378 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 9 Aug 2018 15:31:31 +0200 Subject: [PATCH] DATAJPA-1392 - Further improvements in Metamodel handling in MappingContext implementation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../mapping/JpaMetamodelMappingContext.java | 85 +++++++++++++------ .../jpa/mapping/JpaPersistentEntityImpl.java | 11 +-- .../mapping/JpaPersistentPropertyImpl.java | 25 ++---- .../repository/query/AbstractJpaQuery.java | 2 +- .../JpaMetamodelEntityInformation.java | 2 +- .../support/JpaRepositoryFactory.java | 2 +- .../data/jpa/util/JpaMetamodel.java | 10 ++- .../JpaPersistentPropertyImplUnitTests.java | 2 +- .../data/jpa/util/JpaMetamodelUnitTests.java | 2 +- 9 files changed, 87 insertions(+), 54 deletions(-) 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 f60da8f5e..35db7c95d 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java @@ -21,6 +21,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.context.AbstractMappingContext; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.Property; @@ -40,7 +41,7 @@ import org.springframework.util.Assert; public class JpaMetamodelMappingContext extends AbstractMappingContext, JpaPersistentProperty> { - private final Set models; + private final Metamodels models; private final PersistenceProvider persistenceProvider; /** @@ -53,7 +54,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()); } @@ -63,10 +64,7 @@ public class JpaMetamodelMappingContext */ @Override protected JpaPersistentEntityImpl createPersistentEntity(TypeInformation typeInformation) { - - Metamodel metamodel = getMetamodelFor(typeInformation.getType()); - - return new JpaPersistentEntityImpl(typeInformation, persistenceProvider, metamodel); + return new JpaPersistentEntityImpl(typeInformation, persistenceProvider, models.getMetamodel(typeInformation)); } /* @@ -85,36 +83,75 @@ public class JpaMetamodelMappingContext */ @Override protected boolean shouldCreatePersistentEntityFor(TypeInformation type) { - return getMetamodelFor(type.getType()) != null; + return models.isMetamodelManagedType(type); } /** - * 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 metamodels; - try { - model.managedType(type); - return model; - } catch (IllegalArgumentException o_O) { + private Metamodels(Set 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; + } } } diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java index 44a1d02b5..7d02db90b 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java @@ -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 extends BasicPersistentEntity information, ProxyIdAccessor proxyIdAccessor, Metamodel metamodel) { + public JpaPersistentEntityImpl(TypeInformation information, ProxyIdAccessor proxyIdAccessor, + JpaMetamodel metamodel) { super(information, null); @@ -96,7 +97,7 @@ class JpaPersistentEntityImpl extends BasicPersistentEntity associationTargetType; private final boolean updateable; - private final JpaMetamodel metamodel; private final Lazy isIdProperty; + private final Lazy isEntity; /** * Creates a new {@link JpaPersistentPropertyImpl} @@ -107,7 +94,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty owner, SimpleTypeHolder simpleTypeHolder) { super(property, owner, simpleTypeHolder); @@ -117,10 +104,10 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty 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())); } /* @@ -157,7 +144,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty extends JpaEntityInformationSu IdentifierDerivingDirectFieldAccessFallbackBeanWrapper(Class type, Metamodel metamodel) { super(type); this.metamodel = metamodel; - this.jpaMetamodel = new JpaMetamodel(metamodel); + this.jpaMetamodel = JpaMetamodel.of(metamodel); } /** diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index fc583453a..419dc43f8 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -208,7 +208,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()); } /* diff --git a/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java b/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java index 912158b17..e51d5e846 100644 --- a/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java +++ b/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java @@ -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 CACHE = new HashMap<>(4); + private final Metamodel metamodel; private Lazy>> 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}. * 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 59e8a3e1d..2905f83af 100644 --- a/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java @@ -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; diff --git a/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java b/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java index 96e96fa50..36ae80cf9 100644 --- a/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/util/JpaMetamodelUnitTests.java @@ -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(); } }