DATAJPA-1392 - Optimizations in JpaMetamodelMappingContext(FactoryBean) setup.

We now avoid the eager scanning and addition of JPA managed types to create the JpaMetamodelMappingContext. We also avoid repeated lookups in the Metamodel as we assume the one used for the entity will also be the one that has to be used for all properties of that entity.
This commit is contained in:
Oliver Gierke
2018-08-08 21:22:46 +02:00
parent 8047dda884
commit 463174c1d8
5 changed files with 38 additions and 75 deletions

View File

@@ -63,7 +63,10 @@ public class JpaMetamodelMappingContext
*/
@Override
protected <T> JpaPersistentEntityImpl<?> createPersistentEntity(TypeInformation<T> typeInformation) {
return new JpaPersistentEntityImpl<T>(typeInformation, persistenceProvider);
Metamodel metamodel = getMetamodelFor(typeInformation.getType());
return new JpaPersistentEntityImpl<T>(typeInformation, persistenceProvider, metamodel);
}
/*
@@ -73,14 +76,7 @@ public class JpaMetamodelMappingContext
@Override
protected JpaPersistentProperty createPersistentProperty(Property property, JpaPersistentEntityImpl<?> owner,
SimpleTypeHolder simpleTypeHolder) {
Metamodel metamodel = getMetamodelFor(owner.getType());
if (metamodel == null) {
throw new IllegalStateException(String.format("Metamodel for %s not available!", owner.getType()));
}
return new JpaPersistentPropertyImpl(metamodel, property, owner, simpleTypeHolder);
return new JpaPersistentPropertyImpl(owner.getMetamodel(), property, owner, simpleTypeHolder);
}
/*

View File

@@ -17,6 +17,8 @@ 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.mapping.IdentifierAccessor;
@@ -43,6 +45,7 @@ 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;
/**
* Creates a new {@link JpaPersistentEntityImpl} using the given {@link TypeInformation} and {@link Comparator}.
@@ -50,12 +53,13 @@ class JpaPersistentEntityImpl<T> extends BasicPersistentEntity<T, JpaPersistentP
* @param information must not be {@literal null}.
* @param proxyIdAccessor must not be {@literal null}.
*/
public JpaPersistentEntityImpl(TypeInformation<T> information, ProxyIdAccessor proxyIdAccessor) {
public JpaPersistentEntityImpl(TypeInformation<T> information, ProxyIdAccessor proxyIdAccessor, Metamodel metamodel) {
super(information, null);
Assert.notNull(proxyIdAccessor, "ProxyIdAccessor must not be null!");
this.proxyIdAccessor = proxyIdAccessor;
this.metamodel = metamodel;
}
/*
@@ -92,6 +96,10 @@ class JpaPersistentEntityImpl<T> extends BasicPersistentEntity<T, JpaPersistentP
}
}
Metamodel getMetamodel() {
return metamodel;
}
/**
* {@link IdentifierAccessor} that tries to use a {@link ProxyIdAccessor} for id access to potentially avoid the
* initialization of JPA proxies. We're falling back to the default behavior of {@link IdPropertyIdentifierAccessor}
@@ -112,8 +120,7 @@ class JpaPersistentEntityImpl<T> extends BasicPersistentEntity<T, JpaPersistentP
* @param bean must not be {@literal null}.
* @param proxyIdAccessor must not be {@literal null}.
*/
JpaProxyAwareIdentifierAccessor(JpaPersistentEntity<?> entity, Object bean,
ProxyIdAccessor proxyIdAccessor) {
JpaProxyAwareIdentifierAccessor(JpaPersistentEntity<?> entity, Object bean, ProxyIdAccessor proxyIdAccessor) {
super(entity, bean);

View File

@@ -16,13 +16,13 @@
package org.springframework.data.jpa.repository.config;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.EntityManagerFactory;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.FactoryBean;
@@ -31,6 +31,7 @@ import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext;
import org.springframework.data.util.StreamUtils;
import org.springframework.lang.Nullable;
/**
@@ -43,6 +44,8 @@ import org.springframework.lang.Nullable;
class JpaMetamodelMappingContextFactoryBean extends AbstractFactoryBean<JpaMetamodelMappingContext>
implements ApplicationContextAware {
private static final Logger LOG = LoggerFactory.getLogger(JpaMetamodelMappingContextFactoryBean.class);
private @Nullable ListableBeanFactory beanFactory;
/*
@@ -70,25 +73,17 @@ class JpaMetamodelMappingContextFactoryBean extends AbstractFactoryBean<JpaMetam
@Override
protected JpaMetamodelMappingContext createInstance() throws Exception {
Set<Metamodel> models = getMetamodels();
Set<Class<?>> entitySources = new HashSet<Class<?>>();
for (Metamodel metamodel : models) {
for (ManagedType<?> type : metamodel.getManagedTypes()) {
Class<?> javaType = type.getJavaType();
if (javaType != null) {
entitySources.add(javaType);
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("Initializing JpaMetamodelMappingContext…");
}
JpaMetamodelMappingContext context = new JpaMetamodelMappingContext(models);
context.setInitialEntitySet(entitySources);
JpaMetamodelMappingContext context = new JpaMetamodelMappingContext(getMetamodels());
context.initialize();
if (LOG.isDebugEnabled()) {
LOG.debug("Finished initializing JpaMetamodelMappingContext!");
}
return context;
}
@@ -105,12 +100,9 @@ class JpaMetamodelMappingContextFactoryBean extends AbstractFactoryBean<JpaMetam
Collection<EntityManagerFactory> factories = BeanFactoryUtils
.beansOfTypeIncludingAncestors(beanFactory, EntityManagerFactory.class).values();
Set<Metamodel> metamodels = new HashSet<Metamodel>(factories.size());
for (EntityManagerFactory emf : factories) {
metamodels.add(emf.getMetamodel());
}
return metamodels;
return factories.stream() //
.map(EntityManagerFactory::getMetamodel) //
.collect(StreamUtils.toUnmodifiableSet());
}
}

View File

@@ -16,16 +16,15 @@
package org.springframework.data.jpa.util;
import java.util.Collection;
import java.util.Collections;
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.data.util.Lazy;
import org.springframework.data.util.StreamUtils;
import org.springframework.util.Assert;
/**
@@ -38,7 +37,7 @@ public class JpaMetamodel {
private final Metamodel metamodel;
private Optional<Collection<Class<?>>> managedTypes = Optional.empty();
private Lazy<Collection<Class<?>>> managedTypes;
/**
* Creates a new {@link JpaMetamodel} for the given JPA {@link Metamodel}.
@@ -50,6 +49,10 @@ public class JpaMetamodel {
Assert.notNull(metamodel, "Metamodel must not be null!");
this.metamodel = metamodel;
this.managedTypes = Lazy.of(() -> metamodel.getManagedTypes().stream() //
.map(ManagedType::getJavaType) //
.filter(it -> it != null) //
.collect(StreamUtils.toUnmodifiableSet()));
}
/**
@@ -62,7 +65,7 @@ public class JpaMetamodel {
Assert.notNull(type, "Type must not be null!");
return getManagedTypes().contains(type);
return managedTypes.get().contains(type);
}
/**
@@ -84,35 +87,6 @@ public class JpaMetamodel {
.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()}.
*
* @return all managed types.
* @see <a href="https://hibernate.atlassian.net/browse/HHH-10968">HHH-10968</a>
*/
private Collection<Class<?>> getManagedTypes() {
if (!managedTypes.isPresent()) {
Set<ManagedType<?>> managedTypes = metamodel.getManagedTypes();
Set<Class<?>> types = new HashSet<Class<?>>(managedTypes.size());
for (ManagedType<?> managedType : metamodel.getManagedTypes()) {
Class<?> type = managedType.getJavaType();
if (type != null) {
types.add(type);
}
}
this.managedTypes = Optional.of(Collections.unmodifiableSet(types));
}
return this.managedTypes.get();
}
/**
* Returns the {@link SingularAttribute} representing the identifier of the given {@link EntityType} if it contains a
* singular one.

View File

@@ -22,10 +22,8 @@ import static org.mockito.Mockito.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import javax.persistence.EntityManagerFactory;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import org.junit.Rule;
@@ -100,13 +98,9 @@ public class JpaRepositoryConfigExtensionUnitTests {
ApplicationContext context = mock(ApplicationContext.class);
EntityManagerFactory emf = mock(EntityManagerFactory.class);
Metamodel metamodel = mock(Metamodel.class);
ManagedType<?> managedType = mock(ManagedType.class);
Set<ManagedType<?>> managedTypes = Collections.<ManagedType<?>> singleton(managedType);
when(context.getBeansOfType(EntityManagerFactory.class)).thenReturn(Collections.singletonMap("emf", emf));
when(emf.getMetamodel()).thenReturn(metamodel);
when(metamodel.getManagedTypes()).thenReturn(managedTypes);
JpaMetamodelMappingContextFactoryBean factoryBean = new JpaMetamodelMappingContextFactoryBean();
factoryBean.setApplicationContext(context);